mirror of https://github.com/tiangolo/fastapi.git
♻️ Re-format tutorials, files names and tests
for tutorial files
This commit is contained in:
parent
dcb076b752
commit
00e2e544c7
|
|
@ -1,13 +1,13 @@
|
|||
Coming soon...
|
||||
|
||||
```Python
|
||||
{!./tutorial/src/bigger-applications/tutorial001.py!}
|
||||
{!./tutorial/src/bigger_applications/app/routers/tutorial001.py!}
|
||||
```
|
||||
|
||||
```Python
|
||||
{!./tutorial/src/bigger-applications/tutorial002.py!}
|
||||
{!./tutorial/src/bigger_applications/app/routers/tutorial002.py!}
|
||||
```
|
||||
|
||||
```Python
|
||||
{!./tutorial/src/bigger-applications/tutorial003.py!}
|
||||
{!./tutorial/src/bigger_applications/app/tutorial003.py!}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ Now that we have seen how to use `Path` and `Query`, let's see more advanced use
|
|||
|
||||
First, of course, you can mix `Path`, `Query` and request body parameter declarations freely and **FastAPI** will know what to do.
|
||||
|
||||
```Python hl_lines="17 18 19"
|
||||
{!./tutorial/src/body-multiple-params/tutorial001.py!}
|
||||
And you can also declare body parameters as optional, by setting the default to `None`:
|
||||
|
||||
```Python hl_lines="18 19 20"
|
||||
{!./tutorial/src/body_multiple_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -27,8 +29,8 @@ In the previous example, the path operations would expect a JSON body with the a
|
|||
|
||||
But you can also declare multiple body parameters, e.g. `item` and `user`:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!./tutorial/src/body-multiple-params/tutorial002.py!}
|
||||
```Python hl_lines="21"
|
||||
{!./tutorial/src/body_multiple_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** will notice that there are more than one body parameter in the function (two parameters that are Pydantic models).
|
||||
|
|
@ -69,8 +71,8 @@ If you declare it as is, because it is a singular value, **FastAPI** will assume
|
|||
But you can instruct **FastAPI** to treat it as another body key using `Body`:
|
||||
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./tutorial/src/body-multiple-params/tutorial003.py!}
|
||||
```Python hl_lines="22"
|
||||
{!./tutorial/src/body_multiple_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** will expect a body like:
|
||||
|
|
@ -106,8 +108,8 @@ q: str = None
|
|||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="25"
|
||||
{!./tutorial/src/body-multiple-params/tutorial004.py!}
|
||||
```Python hl_lines="27"
|
||||
{!./tutorial/src/body_multiple_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -128,8 +130,8 @@ item: Item = Body(..., embed=True)
|
|||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="15"
|
||||
{!./tutorial/src/body-multiple-params/tutorial005.py!}
|
||||
```Python hl_lines="16"
|
||||
{!./tutorial/src/body_multiple_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
In this case **FastAPI** will expect a body like:
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ With **FastAPI**, you can define, validate, document, and use arbitrarily deeply
|
|||
|
||||
You can define an attribute to be a subtype. For example, a Python `list`:
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!./tutorial/src/body-nested-models/tutorial001.py!}
|
||||
```Python hl_lines="13"
|
||||
{!./tutorial/src/body_nested_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
This will make `tags` be a list of items. Although it doesn't declare the type of each of the items.
|
||||
|
|
@ -19,7 +19,7 @@ But Python has a specific way to declare lists with subtypes:
|
|||
First, import `List` from standard Python's `typing` module:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/body-nested-models/tutorial002.py!}
|
||||
{!./tutorial/src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Declare a `List` with a subtype
|
||||
|
|
@ -41,8 +41,8 @@ Use that same standard syntax for model attributes with subtypes.
|
|||
|
||||
So, in our example, we can make `tags` be specifically a "list of strings":
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!./tutorial/src/body-nested-models/tutorial002.py!}
|
||||
```Python hl_lines="15"
|
||||
{!./tutorial/src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Set types
|
||||
|
|
@ -53,8 +53,8 @@ And Python has a special data type for sets of unique items, the `set`.
|
|||
|
||||
Then we can import `Set` and declare `tags` as a `set` of `str`:
|
||||
|
||||
```Python hl_lines="1 14"
|
||||
{!./tutorial/src/body-nested-models/tutorial003.py!}
|
||||
```Python hl_lines="1 15"
|
||||
{!./tutorial/src/body_nested_models/tutorial003.py!}
|
||||
```
|
||||
|
||||
With this, even if you receive a request with duplicate data, it will be converted to a set of unique items.
|
||||
|
|
@ -77,16 +77,16 @@ All that, arbitrarily nested.
|
|||
|
||||
For example, we can define an `Image` model:
|
||||
|
||||
```Python hl_lines="9 10 11"
|
||||
{!./tutorial/src/body-nested-models/tutorial004.py!}
|
||||
```Python hl_lines="10 11 12"
|
||||
{!./tutorial/src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
### Use the submodel as a type
|
||||
|
||||
And then we can use it as the type of an attribute:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!./tutorial/src/body-nested-models/tutorial004.py!}
|
||||
```Python hl_lines="21"
|
||||
{!./tutorial/src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
This would mean that **FastAPI** would expect a body similar to:
|
||||
|
|
@ -120,8 +120,8 @@ To see all the options you have, checkout the docs for <a href="https://pydantic
|
|||
|
||||
For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `UrlStr`:
|
||||
|
||||
```Python hl_lines="5 11"
|
||||
{!./tutorial/src/body-nested-models/tutorial005.py!}
|
||||
```Python hl_lines="4 12"
|
||||
{!./tutorial/src/body_nested_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.
|
||||
|
|
@ -130,8 +130,8 @@ The string will be checked to be a valid URL, and documented in JSON Schema / Op
|
|||
|
||||
You can also use Pydantic models as subtypes of `list`, `set`, etc:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./tutorial/src/body-nested-models/tutorial006.py!}
|
||||
```Python hl_lines="22"
|
||||
{!./tutorial/src/body_nested_models/tutorial006.py!}
|
||||
```
|
||||
|
||||
This will expect (convert, validate, document, etc) a JSON body like:
|
||||
|
|
@ -167,8 +167,8 @@ This will expect (convert, validate, document, etc) a JSON body like:
|
|||
|
||||
You can define arbitrarily deeply nested models:
|
||||
|
||||
```Python hl_lines="10 15 21 24 28"
|
||||
{!./tutorial/src/body-nested-models/tutorial007.py!}
|
||||
```Python hl_lines="11 16 22 25 29"
|
||||
{!./tutorial/src/body_nested_models/tutorial007.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -184,8 +184,8 @@ images: List[Image]
|
|||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!./tutorial/src/body-nested-models/tutorial008.py!}
|
||||
```Python hl_lines="17"
|
||||
{!./tutorial/src/body_nested_models/tutorial008.py!}
|
||||
```
|
||||
|
||||
## Editor support everywhere
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ The same way you can declare additional validation and metadata in path operatio
|
|||
|
||||
First, you have to import it:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./tutorial/src/body-schema/tutorial001.py!}
|
||||
```Python hl_lines="3"
|
||||
{!./tutorial/src/body_schema/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
|
@ -16,8 +16,8 @@ First, you have to import it:
|
|||
|
||||
You can then use `Schema` with model attributes:
|
||||
|
||||
```Python hl_lines="9 10"
|
||||
{!./tutorial/src/body-schema/tutorial001.py!}
|
||||
```Python hl_lines="10 11"
|
||||
{!./tutorial/src/body_schema/tutorial001.py!}
|
||||
```
|
||||
|
||||
`Schema` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
|
||||
|
|
@ -44,8 +44,8 @@ If you know JSON Schema and want to add extra information appart from what we ha
|
|||
|
||||
For example, you can use that functionality to pass a <a href="http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.5" target="_blank">JSON Schema example</a> field to a body request JSON Schema:
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25"
|
||||
{!./tutorial/src/body-schema/tutorial002.py!}
|
||||
```Python hl_lines="21 22 23 24 25 26"
|
||||
{!./tutorial/src/body_schema/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ To declare a request body, you use <a href="https://pydantic-docs.helpmanual.io/
|
|||
|
||||
First, you need to import `BaseModel` from `pydantic`:
|
||||
|
||||
```Python hl_lines="2"
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ Then you declare your data model as a class that inherits from `BaseModel`.
|
|||
|
||||
Use standard Python types for all the attributes:
|
||||
|
||||
```Python hl_lines="5 6 7 8 9"
|
||||
```Python hl_lines="6 7 8 9 10"
|
||||
{!./tutorial/src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
|
|||
|
||||
To add it to your path operation, declare it the same way you declared path and query parameters:
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="17"
|
||||
{!./tutorial/src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
|
|||
|
||||
Inside of the function, you can access all the attributes of the model object directly:
|
||||
|
||||
```Python hl_lines="19"
|
||||
```Python hl_lines="20"
|
||||
{!./tutorial/src/body/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ You can declare path parameters and body requests at the same time.
|
|||
|
||||
**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
|
||||
|
||||
```Python hl_lines="15 16"
|
||||
```Python hl_lines="16 17"
|
||||
{!./tutorial/src/body/tutorial003.py!}
|
||||
```
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ You can also declare **body**, **path** and **query** parameters, all at the sam
|
|||
|
||||
**FastAPI** will recognize each of them and take the data from the correct place.
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="17"
|
||||
{!./tutorial/src/body/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ You can define Cookie parameters the same way you define `Query` and `Path` para
|
|||
First import `Cookie`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/cookie-params/tutorial001.py!}
|
||||
{!./tutorial/src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Declare `Cookie` parameteres
|
||||
|
|
@ -15,7 +15,7 @@ Then declare the cookie parameters using the same structure as with `Path` and `
|
|||
The first value is the default value, you can pass all the extra validation or annotation parameteres:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/cookie-params/tutorial001.py!}
|
||||
{!./tutorial/src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ For example, if you are squeezing performance, you can use `ujson` and set the r
|
|||
|
||||
### Import `UJSONResponse`
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./tutorial/src/custom-response/tutorial001.py!}
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/custom_response/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -24,8 +24,8 @@ For example, if you are squeezing performance, you can use `ujson` and set the r
|
|||
|
||||
Make your path operation use `UJSONResponse` as the response class using the parameter `content_type`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/custom-response/tutorial001.py!}
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/custom_response/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -39,8 +39,8 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
|||
|
||||
### Import `HTMLResponse`
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./tutorial/src/custom-response/tutorial002.py!}
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -51,8 +51,8 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
|||
|
||||
Pass `HTMLResponse` as the parameter `content_type` of your path operation:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/custom-response/tutorial002.py!}
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -71,8 +71,8 @@ If you return an object that is an instance of Starlette's `Response`, it will b
|
|||
|
||||
The same example from above, returning an `HTMLResponse`, could look like:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/custom-response/tutorial003.py!}
|
||||
```Python hl_lines="8 20"
|
||||
{!./tutorial/src/custom_response/tutorial003.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -92,8 +92,8 @@ The `content_type` class will then be used only to document the OpenAPI path ope
|
|||
|
||||
For example, it could be something like:
|
||||
|
||||
```Python hl_lines="7 23"
|
||||
{!./tutorial/src/custom-response/tutorial004.py!}
|
||||
```Python hl_lines="8 19 22"
|
||||
{!./tutorial/src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
In this example, the function `generate_html_response()` already generates a Starlette `Response` instead of the HTML in a `str`.
|
||||
|
|
@ -104,8 +104,8 @@ By returning the result of calling `generate_html_response()`, you are already r
|
|||
|
||||
But by declaring it also in the path operation decorator:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./tutorial/src/custom-response/tutorial004.py!}
|
||||
```Python hl_lines="22"
|
||||
{!./tutorial/src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
#### OpenAPI knows how to document it
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Let's use them here too.
|
|||
|
||||
Create a model for the common parameters (and don't pay attention to the rest, for now):
|
||||
|
||||
```Python hl_lines="10 11 12 13"
|
||||
```Python hl_lines="11 12 13 14"
|
||||
{!./tutorial/src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ Create a model for the common parameters (and don't pay attention to the rest, f
|
|||
|
||||
Now we can return a Pydantic model from the dependency ("dependable") with the same data as the dict before:
|
||||
|
||||
```Python hl_lines="17"
|
||||
```Python hl_lines="18"
|
||||
{!./tutorial/src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ commons: CommonQueryParams = Depends(common_parameters)
|
|||
|
||||
It won't be interpreted as a JSON request `Body` because we are using `Depends`:
|
||||
|
||||
```Python hl_lines="21"
|
||||
```Python hl_lines="22"
|
||||
{!./tutorial/src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ It won't be interpreted as a JSON request `Body` because we are using `Depends`:
|
|||
|
||||
And now we can use that model in our code, with all the lovable editor support:
|
||||
|
||||
```Python hl_lines="23 24 25"
|
||||
```Python hl_lines="24 25 26"
|
||||
{!./tutorial/src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ Continuing with the previous example, it will be common to have more than one re
|
|||
|
||||
This is especially the case for user models, because:
|
||||
|
||||
* The **input model** needs to be able to have a password
|
||||
* The **output model** should do not have a password
|
||||
* The **database model** would probably need to have a hashed password
|
||||
* The **input model** needs to be able to have a password.
|
||||
* The **output model** should do not have a password.
|
||||
* The **database model** would probably need to have a hashed password.
|
||||
|
||||
!!! danger
|
||||
Never store user's plaintext passwords. Always store a secure hash that you can then verify.
|
||||
|
|
@ -13,8 +13,8 @@ This is especially the case for user models, because:
|
|||
|
||||
Here's a general idea of how the models could look like with their password fields and the places where they are used:
|
||||
|
||||
```Python hl_lines="8 10 15 21 23 32 34 39 40"
|
||||
{!./tutorial/src/extra-models/tutorial001.py!}
|
||||
```Python hl_lines="9 11 16 22 24 33 35 40 41"
|
||||
{!./tutorial/src/extra_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
|
@ -36,8 +36,8 @@ All the data conversion, validation, documentation, etc. will still work as norm
|
|||
|
||||
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
|
||||
|
||||
```Python hl_lines="8 14 15 18 19 22 23"
|
||||
{!./tutorial/src/extra-models/tutorial002.py!}
|
||||
```Python hl_lines="9 15 16 19 20 23 24"
|
||||
{!./tutorial/src/extra_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
The simplest FastAPI file could look like this:
|
||||
|
||||
```Python
|
||||
{!tutorial/src/first-steps/tutorial001.py!}
|
||||
{!tutorial/src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
Copy that to a file `main.py`.
|
||||
|
|
@ -89,7 +89,7 @@ It will show a JSON starting with something like:
|
|||
### Step 1: import `FastAPI`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!tutorial/src/first-steps/tutorial001.py!}
|
||||
{!tutorial/src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
`FastAPI` is a Python class that provides all the functionality for your API.
|
||||
|
|
@ -102,7 +102,7 @@ It will show a JSON starting with something like:
|
|||
### Step 2: create a `FastAPI` "instance"
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!tutorial/src/first-steps/tutorial001.py!}
|
||||
{!tutorial/src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
Here the `app` variable will be an "instance" of the class `FastAPI`.
|
||||
|
|
@ -118,7 +118,7 @@ uvicorn main:app --debug
|
|||
If you create your app like:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!tutorial/src/first-steps/tutorial002.py!}
|
||||
{!tutorial/src/first_steps/tutorial002.py!}
|
||||
```
|
||||
|
||||
And put it in a file `main.py`, then you would call `uvicorn` like:
|
||||
|
|
@ -188,7 +188,7 @@ We are going to call them "operations" too.
|
|||
#### Define a path operation function
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!tutorial/src/first-steps/tutorial001.py!}
|
||||
{!tutorial/src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to:
|
||||
|
|
@ -221,7 +221,7 @@ And the more exotic ones:
|
|||
### Step 4: define the path operation function
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!tutorial/src/first-steps/tutorial001.py!}
|
||||
{!tutorial/src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
This is a Python function.
|
||||
|
|
@ -235,7 +235,7 @@ In this case, it is an `async` function.
|
|||
You could also define it as a normal function instead of `async def`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!tutorial/src/first-steps/tutorial003.py!}
|
||||
{!tutorial/src/first_steps/tutorial003.py!}
|
||||
```
|
||||
|
||||
To know the difference, read the section about [Concurrency and `async` / `await`](/async/).
|
||||
|
|
@ -243,7 +243,7 @@ To know the difference, read the section about [Concurrency and `async` / `await
|
|||
### Step 5: return the content
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!tutorial/src/first-steps/tutorial001.py!}
|
||||
{!tutorial/src/first_steps/tutorial001.py!}
|
||||
```
|
||||
|
||||
You can return a `dict`, `list`, singular values as `str`, `int`, etc.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ You can define Header parameters the same way you define `Query`, `Path` and `Co
|
|||
First import `Header`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/header-params/tutorial001.py!}
|
||||
{!./tutorial/src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Declare `Header` parameteres
|
||||
|
|
@ -15,7 +15,7 @@ Then declare the header parameters using the same structure as with `Path`, `Que
|
|||
The first value is the default value, you can pass all the extra validation or annotation parameteres:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/header-params/tutorial001.py!}
|
||||
{!./tutorial/src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -41,7 +41,7 @@ So, you can use `user_agent` as you normally would in Python code, instead of ne
|
|||
If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/header-params/tutorial002.py!}
|
||||
{!./tutorial/src/header_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ You can adapt it to any other NoSQL database like:
|
|||
|
||||
For now, don't pay attention to the rest, only the imports:
|
||||
|
||||
```Python hl_lines="6 7 8"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
```Python hl_lines="5 6 7"
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Define a constant to use as a "document type"
|
||||
|
|
@ -25,7 +25,7 @@ We will use it later as a fixed field `type` in our documents.
|
|||
This is not required by Couchbase, but is a good practice that will help you afterwards.
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Add a function to get a `Bucket`
|
||||
|
|
@ -49,8 +49,8 @@ This utility function will:
|
|||
* Set defaults for timeouts.
|
||||
* Return it.
|
||||
|
||||
```Python hl_lines="13 14 15 16 17 18 19 20"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
```Python hl_lines="13 14 15 16 17 18 19 20 21 22"
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create Pydantic models
|
||||
|
|
@ -61,8 +61,8 @@ As **Couchbase** "documents" are actually just "JSON objects", we can model them
|
|||
|
||||
First, let's create a `User` model:
|
||||
|
||||
```Python hl_lines="23 24 25 26 27"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
```Python hl_lines="25 26 27 28 29"
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
We will use this model in our path operation function, so, we don't include in it the `hashed_password`.
|
||||
|
|
@ -75,8 +75,8 @@ This will have the data that is actually stored in the database.
|
|||
|
||||
We don't create it as a subclass of Pydantic's `BaseModel` but as a subclass of our own `User`, because it will have all the attributes in `User` plus a couple more:
|
||||
|
||||
```Python hl_lines="30 31 32"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
```Python hl_lines="32 33 34"
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -96,8 +96,8 @@ Now create a function that will:
|
|||
|
||||
By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your path operation function, you can more easily re-use it in multiple parts and also add <abbr title="Automated test, written in code, that checks if another piece of code is working correctly.">unit tests</abbr> for it:
|
||||
|
||||
```Python hl_lines="35 36 37 38 39 40 41"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
```Python hl_lines="37 38 39 40 41 42 43"
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
### f-strings
|
||||
|
|
@ -131,8 +131,8 @@ UserInDB(username="johndoe", hashed_password="some_hash")
|
|||
|
||||
### Create the `FastAPI` app
|
||||
|
||||
```Python hl_lines="45"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
```Python hl_lines="47"
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Create the path operation function
|
||||
|
|
@ -141,8 +141,8 @@ As our code is calling Couchbase and we are not using the <a href="https://docs.
|
|||
|
||||
Also, Couchbase recommends not using a single `Bucket` object in multiple "<abbr title="A sequence of code being executed by the program, while at the same time, or at intervals, there can be others being executed too.">thread</abbr>s", so, we can get just get the bucket directly and pass it to our utility functions:
|
||||
|
||||
```Python hl_lines="48 49 50 51 52"
|
||||
{!./tutorial/src/nosql-databases/tutorial001.py!}
|
||||
```Python hl_lines="50 51 52 53 54"
|
||||
{!./tutorial/src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ You can set the OpenAPI `operationId` to be used in your path operation with the
|
|||
You would have to make sure that it is unique for each operation.
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./tutorial/src/path-operation-advanced-configuration/tutorial001.py!}
|
||||
{!./tutorial/src/path_operation_advanced_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Exclude from OpenAPI
|
||||
|
|
@ -16,5 +16,5 @@ You would have to make sure that it is unique for each operation.
|
|||
To exclude a path operation from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`;
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./tutorial/src/path-operation-advanced-configuration/tutorial002.py!}
|
||||
{!./tutorial/src/path_operation_advanced_configuration/tutorial002.py!}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ You can pass directly the `int` code, like `404`.
|
|||
|
||||
But if you don't remember what each number code is for, you can use the shortcut constants from `starlette`:
|
||||
|
||||
```Python hl_lines="5 18"
|
||||
{!./tutorial/src/path-operation-configuration/tutorial001.py!}
|
||||
```Python hl_lines="4 19"
|
||||
{!./tutorial/src/path_operation_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
That status code will be used in the response and will be added to the OpenAPI schema.
|
||||
|
|
@ -22,8 +22,8 @@ That status code will be used in the response and will be added to the OpenAPI s
|
|||
|
||||
You can add tags to your path operation, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
{!./tutorial/src/path-operation-configuration/tutorial002.py!}
|
||||
```Python hl_lines="18 23 28"
|
||||
{!./tutorial/src/path_operation_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
They will be added to the OpenAPI schema and used by the automatic documentation interfaces:
|
||||
|
|
@ -34,16 +34,16 @@ They will be added to the OpenAPI schema and used by the automatic documentation
|
|||
|
||||
You can add a `summary` and `description`:
|
||||
|
||||
```Python hl_lines="20 21"
|
||||
{!./tutorial/src/path-operation-configuration/tutorial003.py!}
|
||||
```Python hl_lines="21 22"
|
||||
{!./tutorial/src/path_operation_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Description from docstring
|
||||
|
||||
As descriptions tend to be long and cover multiple lines, you can declare the path operation description in the function <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation">docstring</abbr> and **FastAPI** will read it from there.
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25 26 27"
|
||||
{!./tutorial/src/path-operation-configuration/tutorial004.py!}
|
||||
```Python hl_lines="20 21 22 23 24 25 26 27 28"
|
||||
{!./tutorial/src/path_operation_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
It will be used in the interactive docs:
|
||||
|
|
@ -57,8 +57,8 @@ It will be used in the interactive docs:
|
|||
|
||||
You can specify the response description with the parameter `response_description`:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./tutorial/src/path-operation-configuration/tutorial005.py!}
|
||||
```Python hl_lines="22"
|
||||
{!./tutorial/src/path_operation_configuration/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -77,7 +77,7 @@ If you need to mark a path operation as <abbr title="obsolete, recommended not t
|
|||
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!./tutorial/src/path-operation-configuration/tutorial006.py!}
|
||||
{!./tutorial/src/path_operation_configuration/tutorial006.py!}
|
||||
```
|
||||
|
||||
It will be clearly marked as deprecated in the interactive docs:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ The same way you can declare more validations and metadata for query parameters
|
|||
First, import `Path` from `fastapi`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/path-params-numeric-validations/tutorial001.py!}
|
||||
{!./tutorial/src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Declare metadata
|
||||
|
|
@ -15,7 +15,7 @@ You can declare all the same parameters as for `Query`.
|
|||
For example, to declare a `title` metadata value for the path parameter `item_id` you can type:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/path-params-numeric-validations/tutorial001.py!}
|
||||
{!./tutorial/src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -42,7 +42,7 @@ It doesn't matter for **FastAPI**. It will detect the parameters by their names,
|
|||
So, you can declare your function as:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/path-params-numeric-validations/tutorial002.py!}
|
||||
{!./tutorial/src/path_params_numeric_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Order the parameters as you need, tricks
|
||||
|
|
@ -54,7 +54,7 @@ Pass `*`, as the first parameter of the function.
|
|||
Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Even if they don't have a default value.
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/path-params-numeric-validations/tutorial003.py!}
|
||||
{!./tutorial/src/path_params_numeric_validations/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Number validations: greater than or equal
|
||||
|
|
@ -64,7 +64,7 @@ With `Query` and `Path` (and other's you'll see later) you can declare string co
|
|||
Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`.
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/path-params-numeric-validations/tutorial004.py!}
|
||||
{!./tutorial/src/path_params_numeric_validations/tutorial004.py!}
|
||||
```
|
||||
## Number validations: greater than and less than or equal
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ The same applies for:
|
|||
* `le`: `l`ess than or `e`qual
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!./tutorial/src/path-params-numeric-validations/tutorial005.py!}
|
||||
{!./tutorial/src/path_params_numeric_validations/tutorial005.py!}
|
||||
```
|
||||
|
||||
## Number validations: floats, greater than and less than
|
||||
|
|
@ -88,7 +88,7 @@ So, `0.5` would be a valid value. But `0.0` or `0` would not.
|
|||
And the same for <abbr title="less than"><code>lt</code></abbr>.
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!./tutorial/src/path-params-numeric-validations/tutorial006.py!}
|
||||
{!./tutorial/src/path_params_numeric_validations/tutorial006.py!}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
{!./tutorial/src/path-params/tutorial001.py!}
|
||||
{!./tutorial/src/path_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
The value of the path parameter `item_id` will be passed to your function as the argument `item_id`.
|
||||
|
|
@ -17,7 +17,7 @@ So, if you run this example and go to <a href="http://127.0.0.1:8000/items/foo"
|
|||
You can declare the type of a path parameter in the function, using standard Python type annotations:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/path-params/tutorial002.py!}
|
||||
{!./tutorial/src/path_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
In this case, `item_id` is declared to be an `int`.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ But even if you never use **FastAPI**, you would benefit from learning a bit abo
|
|||
Let's start with a simple example:
|
||||
|
||||
```Python
|
||||
{!./tutorial/src/python-types/tutorial001.py!}
|
||||
{!./tutorial/src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
Calling this program outputs:
|
||||
|
|
@ -34,7 +34,7 @@ The function does the following:
|
|||
* <abbr title="Puts them together, as one. With the contents of one after the other.">Concatenates</abbr> them with a space in the middle.
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./tutorial/src/python-types/tutorial001.py!}
|
||||
{!./tutorial/src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Edit it
|
||||
|
|
@ -78,7 +78,7 @@ That's it.
|
|||
Those are the "type hints":
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/python-types/tutorial002.py!}
|
||||
{!./tutorial/src/python_types/tutorial002.py!}
|
||||
```
|
||||
|
||||
That is not the same as declaring default values like would be with:
|
||||
|
|
@ -108,7 +108,7 @@ With that, you can scroll, seeing the options, until you find the one that "ring
|
|||
Check this function, it already has type hints:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/python-types/tutorial003.py!}
|
||||
{!./tutorial/src/python_types/tutorial003.py!}
|
||||
```
|
||||
|
||||
Because the editor knows the types of the variables, you don't only get completion, you also get error checks:
|
||||
|
|
@ -118,7 +118,7 @@ Because the editor knows the types of the variables, you don't only get completi
|
|||
Now you know that you have to fix it, convert `age` to a string with `str(age)`:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./tutorial/src/python-types/tutorial004.py!}
|
||||
{!./tutorial/src/python_types/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ You can use, for example:
|
|||
* `bytes`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/python-types/tutorial005.py!}
|
||||
{!./tutorial/src/python_types/tutorial005.py!}
|
||||
```
|
||||
|
||||
### Types with subtypes
|
||||
|
|
@ -158,7 +158,7 @@ For example, let's define a variable to be a `list` of `str`.
|
|||
From `typing`, import `List` (with a capital `L`):
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/python-types/tutorial006.py!}
|
||||
{!./tutorial/src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
Declare the variable, with the same colon (`:`) syntax.
|
||||
|
|
@ -168,7 +168,7 @@ As the type, put the `List`.
|
|||
As the list is a type that takes a "subtype", you put the subtype in square brackets:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!./tutorial/src/python-types/tutorial006.py!}
|
||||
{!./tutorial/src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
|
||||
|
|
@ -188,7 +188,7 @@ And still, the editor knows it is a `str`, and provides support for that.
|
|||
You would do the same to declare `tuple`s and `set`s:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!./tutorial/src/python-types/tutorial007.py!}
|
||||
{!./tutorial/src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
|
@ -205,7 +205,7 @@ The first subtype is for the keys of the `dict`.
|
|||
The second subtype is for the values of the `dict`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!./tutorial/src/python-types/tutorial008.py!}
|
||||
{!./tutorial/src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
|
@ -222,13 +222,13 @@ You can also declare a class as the type of a variable.
|
|||
Let's say you have a class `Person`, with a name:
|
||||
|
||||
```Python hl_lines="1 2 3"
|
||||
{!./tutorial/src/python-types/tutorial009.py!}
|
||||
{!./tutorial/src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
Then you can declare a variable to be of type `Person`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./tutorial/src/python-types/tutorial009.py!}
|
||||
{!./tutorial/src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
And then, again, you get all the editor support:
|
||||
|
|
@ -251,7 +251,7 @@ And you get all the editor support with that resulting object.
|
|||
Taken from the official Pydantic docs:
|
||||
|
||||
```Python
|
||||
{!./tutorial/src/python-types/tutorial010.py!}
|
||||
{!./tutorial/src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Let's take this application as example:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial001.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
The query parameter `q` is of type `str`, and by default is `None`, so it is optional.
|
||||
|
|
@ -18,7 +18,7 @@ We are going to enforce that even though `q` is optional, whenever it is provide
|
|||
To achieve that, first import `Query` from `fastapi`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial002.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Use `Query` as the default value
|
||||
|
|
@ -26,7 +26,7 @@ To achieve that, first import `Query` from `fastapi`:
|
|||
And now use it as the default value of your parameter, setting the parameter `max_length` to 50:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial002.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
As we have to replace the default value `None` with `Query(None)`, the first parameter to `Query` serves the same purpose of defining that default value.
|
||||
|
|
@ -59,7 +59,7 @@ This will validate the data, show a clear error when the data is not valid, and
|
|||
You can also add a parameter `min_length`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial003.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Add regular expressions
|
||||
|
|
@ -67,7 +67,7 @@ You can also add a parameter `min_length`:
|
|||
You can define a <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</abbr> that the parameter should match:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial004.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial004.py!}
|
||||
```
|
||||
|
||||
This specific regular expression checks that the received parameter value:
|
||||
|
|
@ -87,7 +87,7 @@ The same way that you can pass `None` as the first argument to be used as the de
|
|||
Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial005.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -116,7 +116,7 @@ q: str = Query(None, min_length=3)
|
|||
So, when you need to declare a value as required while using `Query`, you can use `...` as the first argument:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial006.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
|
@ -133,13 +133,13 @@ That information will be included in the generated OpenAPI and used by the docum
|
|||
You can add a `title`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial007.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial007.py!}
|
||||
```
|
||||
|
||||
And a `description`:
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial008.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial008.py!}
|
||||
```
|
||||
|
||||
## Alias parameters
|
||||
|
|
@ -161,7 +161,7 @@ But you still need it to be exactly `item-query`...
|
|||
Then you can declare an `alias`, and that alias is what will be used to find the parameter value:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial009.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial009.py!}
|
||||
```
|
||||
|
||||
## Deprecating parameters
|
||||
|
|
@ -173,7 +173,7 @@ You have to leave it there a while because there are clients using it, but you w
|
|||
Then pass the parameter `deprecated=True` to `Query`:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!./tutorial/src/query-params-str-validations/tutorial010.py!}
|
||||
{!./tutorial/src/query_params_str_validations/tutorial010.py!}
|
||||
```
|
||||
|
||||
The docs will show it like this:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!./tutorial/src/query-params/tutorial001.py!}
|
||||
{!./tutorial/src/query_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters.
|
||||
|
|
@ -62,7 +62,7 @@ The parameter values in your function will be:
|
|||
The same way, you can declare optional query parameters, by setting their default to `None`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params/tutorial002.py!}
|
||||
{!./tutorial/src/query_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
In this case, the function parameter `q` will be optional, and will be `None` by default.
|
||||
|
|
@ -75,7 +75,7 @@ In this case, the function parameter `q` will be optional, and will be `None` by
|
|||
You can also declare `bool` types, and they will be converted:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/query-params/tutorial003.py!}
|
||||
{!./tutorial/src/query_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
In this case, if you go to:
|
||||
|
|
@ -120,7 +120,7 @@ And you don't have to declare them in any specific order.
|
|||
They will be detected by name:
|
||||
|
||||
```Python hl_lines="6 8"
|
||||
{!./tutorial/src/query-params/tutorial004.py!}
|
||||
{!./tutorial/src/query_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
## Required query parameters
|
||||
|
|
@ -132,7 +132,7 @@ If you don't want to add a specific value but just make it optional, set the def
|
|||
But when you want to make a query parameter required, you can just do not declare any default value:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
{!./tutorial/src/query-params/tutorial005.py!}
|
||||
{!./tutorial/src/query_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
Here the query parameter `needy` is a required query parameter of type `str`.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ You can define files to be uploaded by the client using `File`.
|
|||
Import `File` from `fastapi`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/request-files/tutorial001.py!}
|
||||
{!./tutorial/src/request_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Define `File` parameters
|
||||
|
|
@ -13,7 +13,7 @@ Import `File` from `fastapi`:
|
|||
Create file parameters the same way you would for `Body` or `Form`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/request-files/tutorial001.py!}
|
||||
{!./tutorial/src/request_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
The files will be uploaded as form data and you will receive the contents as `bytes`.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ You can define files and form fields at the same time using `File` and `Form`.
|
|||
## Import `File` and `Form`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/request-forms-and-files/tutorial001.py!}
|
||||
{!./tutorial/src/request_forms_and_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Define `File` and `Form` parameters
|
||||
|
|
@ -11,7 +11,7 @@ You can define files and form fields at the same time using `File` and `Form`.
|
|||
Create file and form parameters the same way you would for `Body` or `Query`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/request-forms-and-files/tutorial001.py!}
|
||||
{!./tutorial/src/request_forms_and_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
The files and form fields will be uploaded as form data and you will receive the files and form fields.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ When you need to receive form fields instead of JSON, you can use `Form`.
|
|||
Import `Form` from `fastapi`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./tutorial/src/request-forms/tutorial001.py!}
|
||||
{!./tutorial/src/request_forms/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Define `Form` parameters
|
||||
|
|
@ -13,7 +13,7 @@ Import `Form` from `fastapi`:
|
|||
Create form parameters the same way you would for `Body` or `Query`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/request-forms/tutorial001.py!}
|
||||
{!./tutorial/src/request_forms/tutorial001.py!}
|
||||
```
|
||||
|
||||
For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields.
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ You can declare the model used for the response with the parameter `response_mod
|
|||
* `@app.delete()`
|
||||
* etc.
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./tutorial/src/response-model/tutorial001.py!}
|
||||
```Python hl_lines="18"
|
||||
{!./tutorial/src/response_model/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
|
@ -28,14 +28,14 @@ But most importantly:
|
|||
|
||||
Here we are declaring a `UserIn` model, it will contain a plaintext password:
|
||||
|
||||
```Python hl_lines="8 10"
|
||||
{!./tutorial/src/response-model/tutorial002.py!}
|
||||
```Python hl_lines="9 11"
|
||||
{!./tutorial/src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
And we are using this model to declare our input and the same model to declare our output:
|
||||
|
||||
```Python hl_lines="16 17"
|
||||
{!./tutorial/src/response-model/tutorial002.py!}
|
||||
```Python hl_lines="17 18"
|
||||
{!./tutorial/src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
Now, whenever a browser is creating a user with a password, the API will return the same password in the response.
|
||||
|
|
@ -51,20 +51,20 @@ But if we use sthe same model for another path operation, we could be sending th
|
|||
|
||||
We can instead create an input model with the plaintext password and an output model without it:
|
||||
|
||||
```Python hl_lines="8 10 15"
|
||||
{!./tutorial/src/response-model/tutorial003.py!}
|
||||
```Python hl_lines="9 11 16"
|
||||
{!./tutorial/src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
Here, even though our path operation function is returning the same input user that contains the password:
|
||||
|
||||
```Python hl_lines="23"
|
||||
{!./tutorial/src/response-model/tutorial003.py!}
|
||||
```Python hl_lines="24"
|
||||
{!./tutorial/src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./tutorial/src/response-model/tutorial003.py!}
|
||||
```Python hl_lines="22"
|
||||
{!./tutorial/src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
So, **FastAPI** will take care of filtering out all the data that is not declared in the output model (using Pydantic).
|
||||
|
|
|
|||
|
|
@ -23,16 +23,16 @@ In this example, we'll use **PostgreSQL**.
|
|||
|
||||
For now, don't pay attention to the rest, only the imports:
|
||||
|
||||
```Python hl_lines="3 4 5"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
```Python hl_lines="2 3 4"
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Define the database
|
||||
|
||||
Define the database that SQLAlchemy should connect to:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
```Python hl_lines="7"
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
|
@ -40,14 +40,14 @@ Define the database that SQLAlchemy should connect to:
|
|||
|
||||
## Create the SQLAlchemy `engine`
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
```Python hl_lines="9"
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create a `scoped_session`
|
||||
|
||||
```Python hl_lines="11 12 13"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
```Python hl_lines="10 11 12"
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Very Technical Details"
|
||||
|
|
@ -70,13 +70,13 @@ That way you don't have to declare them explicitly.
|
|||
So, your models will behave very similarly to, for example, Flask-SQLAlchemy.
|
||||
|
||||
```Python hl_lines="15 16 17 18 19"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create the SQLAlchemy `Base` model
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create your application data model
|
||||
|
|
@ -86,7 +86,7 @@ Now this is finally code specific to your app.
|
|||
Here's a user model that will be a table in the database:
|
||||
|
||||
```Python hl_lines="25 26 27 28 29"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Get a user
|
||||
|
|
@ -94,7 +94,7 @@ Here's a user model that will be a table in the database:
|
|||
By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your path operation function, you can more easily re-use it in multiple parts and also add <abbr title="Automated test, written in code, that checks if another piece of code is working correctly.">unit tests</abbr> for it:
|
||||
|
||||
```Python hl_lines="32 33"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Create your **FastAPI** code
|
||||
|
|
@ -104,7 +104,7 @@ Now, finally, here's the standard **FastAPI** code.
|
|||
Create your app and path operation function:
|
||||
|
||||
```Python hl_lines="37 40 41 42 43"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
As we are using SQLAlchemy's `scoped_session`, we don't even have to create a dependency with `Depends`.
|
||||
|
|
@ -132,7 +132,7 @@ user = get_user(username, db_session)
|
|||
Then we should declare the path operation without `async def`, just with a normal `def`:
|
||||
|
||||
```Python hl_lines="41"
|
||||
{!./tutorial/src/sql-databases/tutorial001.py!}
|
||||
{!./tutorial/src/sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Migrations
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ async def read_users():
|
|||
return [{"username": "Foo"}, {"username": "Bar"}]
|
||||
|
||||
|
||||
@router.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"username": username}
|
||||
|
||||
|
||||
@router.get("/users/me")
|
||||
async def read_user_me():
|
||||
return {"username": "fakecurrentuser"}
|
||||
|
||||
|
||||
@router.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"username": username}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
from .tutorial01 import router as users_router
|
||||
from .tutorial02 import router as items_router
|
||||
from .routers.tutorial001 import router as users_router
|
||||
from .routers.tutorial002 import router as items_router
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI, Path
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI, Path
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ class Item(BaseModel):
|
|||
async def update_item(
|
||||
*,
|
||||
item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
|
||||
q: str,
|
||||
q: str = None,
|
||||
item: Item = None,
|
||||
):
|
||||
results = {"item_id": item_id}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel, Schema
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import Body, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from starlette.responses import UJSONResponse
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import FastAPI
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import List
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
from random import choice
|
||||
from typing import List
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import EmailStr
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import EmailStr
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from couchbase import LOCKMODE_WAIT
|
||||
from couchbase.bucket import Bucket
|
||||
from couchbase.cluster import Cluster, PasswordAuthenticator
|
||||
from fastapi import FastAPI
|
||||
|
||||
USERPROFILE_DOC_TYPE = "userprofile"
|
||||
|
||||
|
||||
def get_bucket():
|
||||
cluster = Cluster("couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300")
|
||||
cluster = Cluster(
|
||||
"couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300"
|
||||
)
|
||||
authenticator = PasswordAuthenticator("username", "password")
|
||||
cluster.authenticate(authenticator)
|
||||
bucket: Bucket = cluster.open_bucket("bucket_name", lockmode=LOCKMODE_WAIT)
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue