mirror of https://github.com/tiangolo/fastapi.git
✨ Add include, exclude, and by_alias to path operation methods (#264)
* ✨ Make jsonable_encoder's include and exclude receive sequences * ✨ Add include, exclude, and by_alias to app and router * ✨ Add and update tutorial code with new parameters * 📝 Update docs for new parameters and add docs for updating data * ✅ Add tests for consistency in path operation methods * ✅ Add tests for new parameters and update tests
This commit is contained in:
parent
747ae8210f
commit
7b63bc5551
|
|
@ -0,0 +1,34 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str = None
|
||||||
|
description: str = None
|
||||||
|
price: float = None
|
||||||
|
tax: float = 10.5
|
||||||
|
tags: List[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
items = {
|
||||||
|
"foo": {"name": "Foo", "price": 50.2},
|
||||||
|
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||||
|
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/items/{item_id}", response_model=Item)
|
||||||
|
async def read_item(item_id: str):
|
||||||
|
return items[item_id]
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/items/{item_id}", response_model=Item)
|
||||||
|
async def update_item(item_id: str, item: Item):
|
||||||
|
update_item_encoded = jsonable_encoder(item)
|
||||||
|
items[item_id] = update_item_encoded
|
||||||
|
return update_item_encoded
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str = None
|
||||||
|
description: str = None
|
||||||
|
price: float = None
|
||||||
|
tax: float = 10.5
|
||||||
|
tags: List[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
items = {
|
||||||
|
"foo": {"name": "Foo", "price": 50.2},
|
||||||
|
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||||
|
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/items/{item_id}", response_model=Item)
|
||||||
|
async def read_item(item_id: str):
|
||||||
|
return items[item_id]
|
||||||
|
|
||||||
|
|
||||||
|
@app.patch("/items/{item_id}", response_model=Item)
|
||||||
|
async def update_item(item_id: str, item: Item):
|
||||||
|
stored_item_data = items[item_id]
|
||||||
|
stored_item_model = Item(**stored_item_data)
|
||||||
|
update_data = item.dict(skip_defaults=True)
|
||||||
|
updated_item = stored_item_model.copy(update=update_data)
|
||||||
|
items[item_id] = jsonable_encoder(updated_item)
|
||||||
|
return updated_item
|
||||||
|
|
@ -22,15 +22,5 @@ items = {
|
||||||
|
|
||||||
|
|
||||||
@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
|
@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
|
||||||
def read_item(item_id: str):
|
async def read_item(item_id: str):
|
||||||
return items[item_id]
|
return items[item_id]
|
||||||
|
|
||||||
|
|
||||||
@app.patch("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
|
|
||||||
async def update_item(item_id: str, item: Item):
|
|
||||||
stored_item_data = items[item_id]
|
|
||||||
stored_item_model = Item(**stored_item_data)
|
|
||||||
update_data = item.dict(skip_defaults=True)
|
|
||||||
updated_item = stored_item_model.copy(update=update_data)
|
|
||||||
items[item_id] = updated_item
|
|
||||||
return updated_item
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: str = None
|
||||||
|
price: float
|
||||||
|
tax: float = 10.5
|
||||||
|
|
||||||
|
|
||||||
|
items = {
|
||||||
|
"foo": {"name": "Foo", "price": 50.2},
|
||||||
|
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
|
||||||
|
"baz": {
|
||||||
|
"name": "Baz",
|
||||||
|
"description": "There goes my baz",
|
||||||
|
"price": 50.2,
|
||||||
|
"tax": 10.5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
"/items/{item_id}/name",
|
||||||
|
response_model=Item,
|
||||||
|
response_model_include={"name", "description"},
|
||||||
|
)
|
||||||
|
async def read_item_name(item_id: str):
|
||||||
|
return items[item_id]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
|
||||||
|
async def read_item_public_data(item_id: str):
|
||||||
|
return items[item_id]
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: str = None
|
||||||
|
price: float
|
||||||
|
tax: float = 10.5
|
||||||
|
|
||||||
|
|
||||||
|
items = {
|
||||||
|
"foo": {"name": "Foo", "price": 50.2},
|
||||||
|
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
|
||||||
|
"baz": {
|
||||||
|
"name": "Baz",
|
||||||
|
"description": "There goes my baz",
|
||||||
|
"price": 50.2,
|
||||||
|
"tax": 10.5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
"/items/{item_id}/name",
|
||||||
|
response_model=Item,
|
||||||
|
response_model_include=["name", "description"],
|
||||||
|
)
|
||||||
|
async def read_item_name(item_id: str):
|
||||||
|
return items[item_id]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
|
||||||
|
async def read_item_public_data(item_id: str):
|
||||||
|
return items[item_id]
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
## Update replacing with `PUT`
|
||||||
|
|
||||||
|
To update an item you can use the [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) operation.
|
||||||
|
|
||||||
|
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
|
||||||
|
|
||||||
|
```Python hl_lines="30 31 32 33 34 35"
|
||||||
|
{!./src/body_updates/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
`PUT` is used to receive data that should replace the existing data.
|
||||||
|
|
||||||
|
### Warning about replacing
|
||||||
|
|
||||||
|
That means that if you want to update the item `bar` using `PUT` with a body containing:
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{
|
||||||
|
"name": "Barz",
|
||||||
|
"price": 3,
|
||||||
|
"description": None,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
because it doesn't include the already stored attribute `"tax": 20.2`, the input model would take the default value of `"tax": 10.5`.
|
||||||
|
|
||||||
|
And the data would be saved with that "new" `tax` of `10.5`.
|
||||||
|
|
||||||
|
## Partial updates with `PATCH`
|
||||||
|
|
||||||
|
You can also use the [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) operation to *partially* update data.
|
||||||
|
|
||||||
|
This means that you can send only the data that you want to update, leaving the rest intact.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
`PATCH` is less commonly used and known than `PUT`.
|
||||||
|
|
||||||
|
And many teams use only `PUT`, even for partial updates.
|
||||||
|
|
||||||
|
You are **free** to use them however you want, **FastAPI** doesn't impose any restrictions.
|
||||||
|
|
||||||
|
But this guide shows you, more or less, how they are intended to be used.
|
||||||
|
|
||||||
|
### Using Pydantic's `skip_defaults` parameter
|
||||||
|
|
||||||
|
If you want to receive partial updates, it's very useful to use the parameter `skip_defaults` in Pydantic's model's `.dict()`.
|
||||||
|
|
||||||
|
Like `item.dict(skip_defaults=True)`.
|
||||||
|
|
||||||
|
That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values.
|
||||||
|
|
||||||
|
Then you can use this to generate a `dict` with only the data that was set, omitting default values:
|
||||||
|
|
||||||
|
```Python hl_lines="34"
|
||||||
|
{!./src/body_updates/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Pydantic's `update` parameter
|
||||||
|
|
||||||
|
Now, you can create a copy of the existing model using `.copy()`, and pass the `update` parameter with a `dict` containing the data to update.
|
||||||
|
|
||||||
|
Like `stored_item_model.copy(update=update_data)`:
|
||||||
|
|
||||||
|
```Python hl_lines="35"
|
||||||
|
{!./src/body_updates/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Partial updates recap
|
||||||
|
|
||||||
|
In summary, to apply partial updates you would:
|
||||||
|
|
||||||
|
* (Optionally) use `PATCH` instead of `PUT`.
|
||||||
|
* Retrieve the stored data.
|
||||||
|
* Put that data in a Pydantic model.
|
||||||
|
* Generate a `dict` without default values from the input model (using `skip_defaults`).
|
||||||
|
* This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model.
|
||||||
|
* Create a copy of the stored model, updating it's attributes with the received partial updates (using the `update` parameter).
|
||||||
|
* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`).
|
||||||
|
* This is comparable to using the model's `.dict()` method again, but it makes sure (and converts) the values to data types that can be converted to JSON, for example, `datetime` to `str`.
|
||||||
|
* Save the data to your DB.
|
||||||
|
* Return the updated model.
|
||||||
|
|
||||||
|
```Python hl_lines="30 31 32 33 34 35 36 37"
|
||||||
|
{!./src/body_updates/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
You can actually use this same technique with an HTTP `PUT` operation.
|
||||||
|
|
||||||
|
But the example here uses `PATCH` because it was created for these use cases.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Notice that the input model is still validated.
|
||||||
|
|
||||||
|
So, if you want to receive partial updates that can omit all the attributes, you need to have a model with all the attributes marked as optional (with default values or `None`).
|
||||||
|
|
||||||
|
To distinguish from the models with all optional values for **updates** and models with required values for **creation**, you can use the ideas described in <a href="https://fastapi.tiangolo.com/tutorial/extra-models/" target="_blank">Extra Models</a>.
|
||||||
|
|
@ -13,12 +13,14 @@ You can declare the model used for the response with the parameter `response_mod
|
||||||
!!! note
|
!!! note
|
||||||
Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your path operation function, like all the parameters and body.
|
Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your path operation function, like all the parameters and body.
|
||||||
|
|
||||||
It receives a standard Pydantic model and will:
|
It receives the same type you would declare for a Pydantic model attribute, so, it can be a Pydantic model, but it can also be, e.g. a `list` of Pydantic models, like `List[Item]`.
|
||||||
|
|
||||||
* Convert the output data to the type declarations of the model
|
FastAPI will use this `response_model` to:
|
||||||
* Validate the data
|
|
||||||
* Add a JSON Schema for the response, in the OpenAPI path operation
|
* Convert the output data to its type declaration.
|
||||||
* Will be used by the automatic documentation systems
|
* Validate the data.
|
||||||
|
* Add a JSON Schema for the response, in the OpenAPI path operation.
|
||||||
|
* Will be used by the automatic documentation systems.
|
||||||
|
|
||||||
But most importantly:
|
But most importantly:
|
||||||
|
|
||||||
|
|
@ -45,7 +47,7 @@ Now, whenever a browser is creating a user with a password, the API will return
|
||||||
|
|
||||||
In this case, it might not be a problem, because the user himself is sending the password.
|
In this case, it might not be a problem, because the user himself is sending the password.
|
||||||
|
|
||||||
But if we use the same model for another path operation, we could be sending the passwords of our users to every client.
|
But if we use the same model for another path operation, we could be sending our user's passwords to every client.
|
||||||
|
|
||||||
!!! danger
|
!!! danger
|
||||||
Never send the plain password of a user in a response.
|
Never send the plain password of a user in a response.
|
||||||
|
|
@ -84,7 +86,7 @@ And both models will be used for the interactive API documentation:
|
||||||
|
|
||||||
## Response Model encoding parameters
|
## Response Model encoding parameters
|
||||||
|
|
||||||
If your response model has default values, like:
|
Your response model could have default values, like:
|
||||||
|
|
||||||
```Python hl_lines="11 13 14"
|
```Python hl_lines="11 13 14"
|
||||||
{!./src/response_model/tutorial004.py!}
|
{!./src/response_model/tutorial004.py!}
|
||||||
|
|
@ -94,6 +96,12 @@ If your response model has default values, like:
|
||||||
* `tax: float = None` has a default of `None`.
|
* `tax: float = None` has a default of `None`.
|
||||||
* `tags: List[str] = []` has a default of an empty list: `[]`.
|
* `tags: List[str] = []` has a default of an empty list: `[]`.
|
||||||
|
|
||||||
|
but you might want to omit them from the result if they were not actually stored.
|
||||||
|
|
||||||
|
For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.
|
||||||
|
|
||||||
|
### Use the `response_model_skip_defaults` parameter
|
||||||
|
|
||||||
You can set the *path operation decorator* parameter `response_model_skip_defaults=True`:
|
You can set the *path operation decorator* parameter `response_model_skip_defaults=True`:
|
||||||
|
|
||||||
```Python hl_lines="24"
|
```Python hl_lines="24"
|
||||||
|
|
@ -114,7 +122,7 @@ So, if you send a request to that *path operation* for the item with ID `foo`, t
|
||||||
!!! info
|
!!! info
|
||||||
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/#copying" target="_blank">its `skip_defaults` parameter</a> to achieve this.
|
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/#copying" target="_blank">its `skip_defaults` parameter</a> to achieve this.
|
||||||
|
|
||||||
### Data with values for fields with defaults
|
#### Data with values for fields with defaults
|
||||||
|
|
||||||
But if your data has values for the model's fields with default values, like the item with ID `bar`:
|
But if your data has values for the model's fields with default values, like the item with ID `bar`:
|
||||||
|
|
||||||
|
|
@ -129,7 +137,7 @@ But if your data has values for the model's fields with default values, like the
|
||||||
|
|
||||||
they will be included in the response.
|
they will be included in the response.
|
||||||
|
|
||||||
### Data with the same values as the defaults
|
#### Data with the same values as the defaults
|
||||||
|
|
||||||
If the data has the same values as the default ones, like the item with ID `baz`:
|
If the data has the same values as the default ones, like the item with ID `baz`:
|
||||||
|
|
||||||
|
|
@ -152,34 +160,35 @@ So, they will be included in the JSON response.
|
||||||
|
|
||||||
They can be a list (`[]`), a `float` of `10.5`, etc.
|
They can be a list (`[]`), a `float` of `10.5`, etc.
|
||||||
|
|
||||||
### Use cases
|
### `response_model_include` and `response_model_exclude`
|
||||||
|
|
||||||
This is very useful in several scenarios.
|
You can also use the *path operation decorator* parameters `response_model_include` and `response_model_exclude`.
|
||||||
|
|
||||||
For example if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.
|
They take a `set` of `str` with the name of the attributes to include (omitting the rest) or to exclude (including the rest).
|
||||||
|
|
||||||
### Using Pydantic's `skip_defaults` directly
|
This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output.
|
||||||
|
|
||||||
You can also use your model's `.dict(skip_defaults=True)` in your code.
|
!!! tip
|
||||||
|
But it is still recommended to use the ideas above, using multiple classes, instead of these parameters.
|
||||||
|
|
||||||
For example, you could receive a model object as a body payload, and update your stored data using only the attributes set, not the default ones:
|
This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you use `response_model_include` or `response_model_exclude` to omit some attributes.
|
||||||
|
|
||||||
```Python hl_lines="31 32 33 34 35"
|
```Python hl_lines="29 35"
|
||||||
{!./src/response_model/tutorial004.py!}
|
{!./src/response_model/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
It's common to use the HTTP `PUT` operation to update data.
|
The syntax `{"name", "description"}` creates a `set` with those two values.
|
||||||
|
|
||||||
In theory, `PUT` should be used to "replace" the entire contents.
|
It is equivalent to `set(["name", "description"])`.
|
||||||
|
|
||||||
The less known HTTP `PATCH` operation is also used to update data.
|
#### Using `list`s instead of `set`s
|
||||||
|
|
||||||
But `PATCH` is expected to be used when *partially* updating data. Instead of *replacing* the entire content.
|
If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly:
|
||||||
|
|
||||||
Still, this is just a small detail, and many teams and code bases use `PUT` instead of `PATCH` for all updates, including to *partially* update contents.
|
```Python hl_lines="29 35"
|
||||||
|
{!./src/response_model/tutorial006.py!}
|
||||||
You can use `PUT` or `PATCH` however you wish.
|
```
|
||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
|
||||||
|
|
||||||
from fastapi import routing
|
from fastapi import routing
|
||||||
from fastapi.openapi.docs import (
|
from fastapi.openapi.docs import (
|
||||||
|
|
@ -138,6 +138,9 @@ class FastAPI(Starlette):
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
methods: List[str] = None,
|
methods: List[str] = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -157,6 +160,9 @@ class FastAPI(Starlette):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=methods,
|
methods=methods,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -178,6 +184,9 @@ class FastAPI(Starlette):
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
methods: List[str] = None,
|
methods: List[str] = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -198,6 +207,9 @@ class FastAPI(Starlette):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=methods,
|
methods=methods,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -250,6 +262,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -267,6 +282,9 @@ class FastAPI(Starlette):
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -287,6 +305,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -304,6 +325,9 @@ class FastAPI(Starlette):
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -324,6 +348,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -341,6 +368,9 @@ class FastAPI(Starlette):
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -361,6 +391,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -377,6 +410,9 @@ class FastAPI(Starlette):
|
||||||
response_description=response_description,
|
response_description=response_description,
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
|
|
@ -398,6 +434,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -415,6 +454,9 @@ class FastAPI(Starlette):
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -435,6 +477,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -452,6 +497,9 @@ class FastAPI(Starlette):
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -472,6 +520,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -489,6 +540,9 @@ class FastAPI(Starlette):
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -509,6 +563,9 @@ class FastAPI(Starlette):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -526,6 +583,9 @@ class FastAPI(Starlette):
|
||||||
responses=responses or {},
|
responses=responses or {},
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ def jsonable_encoder(
|
||||||
custom_encoder: dict = {},
|
custom_encoder: dict = {},
|
||||||
sqlalchemy_safe: bool = True,
|
sqlalchemy_safe: bool = True,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
|
if include is not None and not isinstance(include, set):
|
||||||
|
include = set(include)
|
||||||
|
if exclude is not None and not isinstance(exclude, set):
|
||||||
|
exclude = set(exclude)
|
||||||
if isinstance(obj, BaseModel):
|
if isinstance(obj, BaseModel):
|
||||||
encoder = getattr(obj.Config, "json_encoders", custom_encoder)
|
encoder = getattr(obj.Config, "json_encoders", custom_encoder)
|
||||||
return jsonable_encoder(
|
return jsonable_encoder(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
|
||||||
|
|
||||||
from fastapi import params
|
from fastapi import params
|
||||||
from fastapi.dependencies.models import Dependant
|
from fastapi.dependencies.models import Dependant
|
||||||
|
|
@ -33,10 +33,22 @@ from starlette.websockets import WebSocket
|
||||||
|
|
||||||
|
|
||||||
def serialize_response(
|
def serialize_response(
|
||||||
*, field: Field = None, response: Response, skip_defaults: bool = False
|
*,
|
||||||
|
field: Field = None,
|
||||||
|
response: Response,
|
||||||
|
include: Set[str] = None,
|
||||||
|
exclude: Set[str] = set(),
|
||||||
|
by_alias: bool = True,
|
||||||
|
skip_defaults: bool = False,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
|
|
||||||
encoded = jsonable_encoder(response, skip_defaults=skip_defaults)
|
encoded = jsonable_encoder(
|
||||||
|
response,
|
||||||
|
include=include,
|
||||||
|
exclude=exclude,
|
||||||
|
by_alias=by_alias,
|
||||||
|
skip_defaults=skip_defaults,
|
||||||
|
)
|
||||||
if field:
|
if field:
|
||||||
errors = []
|
errors = []
|
||||||
value, errors_ = field.validate(encoded, {}, loc=("response",))
|
value, errors_ = field.validate(encoded, {}, loc=("response",))
|
||||||
|
|
@ -46,7 +58,13 @@ def serialize_response(
|
||||||
errors.extend(errors_)
|
errors.extend(errors_)
|
||||||
if errors:
|
if errors:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
return jsonable_encoder(value, skip_defaults=skip_defaults)
|
return jsonable_encoder(
|
||||||
|
value,
|
||||||
|
include=include,
|
||||||
|
exclude=exclude,
|
||||||
|
by_alias=by_alias,
|
||||||
|
skip_defaults=skip_defaults,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return encoded
|
return encoded
|
||||||
|
|
||||||
|
|
@ -57,7 +75,10 @@ def get_app(
|
||||||
status_code: int = 200,
|
status_code: int = 200,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
response_field: Field = None,
|
response_field: Field = None,
|
||||||
skip_defaults: bool = False,
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
|
response_model_skip_defaults: bool = False,
|
||||||
) -> Callable:
|
) -> Callable:
|
||||||
assert dependant.call is not None, "dependant.call must be a function"
|
assert dependant.call is not None, "dependant.call must be a function"
|
||||||
is_coroutine = asyncio.iscoroutinefunction(dependant.call)
|
is_coroutine = asyncio.iscoroutinefunction(dependant.call)
|
||||||
|
|
@ -97,7 +118,12 @@ def get_app(
|
||||||
raw_response.background = background_tasks
|
raw_response.background = background_tasks
|
||||||
return raw_response
|
return raw_response
|
||||||
response_data = serialize_response(
|
response_data = serialize_response(
|
||||||
field=response_field, response=raw_response, skip_defaults=skip_defaults
|
field=response_field,
|
||||||
|
response=raw_response,
|
||||||
|
include=response_model_include,
|
||||||
|
exclude=response_model_exclude,
|
||||||
|
by_alias=response_model_by_alias,
|
||||||
|
skip_defaults=response_model_skip_defaults,
|
||||||
)
|
)
|
||||||
return response_class(
|
return response_class(
|
||||||
content=response_data,
|
content=response_data,
|
||||||
|
|
@ -155,6 +181,9 @@ class APIRoute(routing.Route):
|
||||||
name: str = None,
|
name: str = None,
|
||||||
methods: List[str] = None,
|
methods: List[str] = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -215,6 +244,9 @@ class APIRoute(routing.Route):
|
||||||
methods = ["GET"]
|
methods = ["GET"]
|
||||||
self.methods = methods
|
self.methods = methods
|
||||||
self.operation_id = operation_id
|
self.operation_id = operation_id
|
||||||
|
self.response_model_include = response_model_include
|
||||||
|
self.response_model_exclude = response_model_exclude
|
||||||
|
self.response_model_by_alias = response_model_by_alias
|
||||||
self.response_model_skip_defaults = response_model_skip_defaults
|
self.response_model_skip_defaults = response_model_skip_defaults
|
||||||
self.include_in_schema = include_in_schema
|
self.include_in_schema = include_in_schema
|
||||||
self.response_class = response_class
|
self.response_class = response_class
|
||||||
|
|
@ -236,7 +268,10 @@ class APIRoute(routing.Route):
|
||||||
status_code=self.status_code,
|
status_code=self.status_code,
|
||||||
response_class=self.response_class,
|
response_class=self.response_class,
|
||||||
response_field=self.response_field,
|
response_field=self.response_field,
|
||||||
skip_defaults=self.response_model_skip_defaults,
|
response_model_include=self.response_model_include,
|
||||||
|
response_model_exclude=self.response_model_exclude,
|
||||||
|
response_model_by_alias=self.response_model_by_alias,
|
||||||
|
response_model_skip_defaults=self.response_model_skip_defaults,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -258,6 +293,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
methods: List[str] = None,
|
methods: List[str] = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -277,6 +315,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=methods,
|
methods=methods,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -299,6 +340,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
methods: List[str] = None,
|
methods: List[str] = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -319,6 +363,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=methods,
|
methods=methods,
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -374,6 +421,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=route.deprecated,
|
deprecated=route.deprecated,
|
||||||
methods=route.methods,
|
methods=route.methods,
|
||||||
operation_id=route.operation_id,
|
operation_id=route.operation_id,
|
||||||
|
response_model_include=route.response_model_include,
|
||||||
|
response_model_exclude=route.response_model_exclude,
|
||||||
|
response_model_by_alias=route.response_model_by_alias,
|
||||||
response_model_skip_defaults=route.response_model_skip_defaults,
|
response_model_skip_defaults=route.response_model_skip_defaults,
|
||||||
include_in_schema=route.include_in_schema,
|
include_in_schema=route.include_in_schema,
|
||||||
response_class=route.response_class,
|
response_class=route.response_class,
|
||||||
|
|
@ -410,6 +460,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -429,6 +482,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -449,6 +505,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -467,6 +526,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["PUT"],
|
methods=["PUT"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -487,6 +549,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -505,6 +570,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["POST"],
|
methods=["POST"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -525,6 +593,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -543,6 +614,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["DELETE"],
|
methods=["DELETE"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -563,6 +637,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -581,6 +658,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["OPTIONS"],
|
methods=["OPTIONS"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -601,6 +681,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -619,6 +702,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["HEAD"],
|
methods=["HEAD"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -639,6 +725,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -657,6 +746,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["PATCH"],
|
methods=["PATCH"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
@ -677,6 +769,9 @@ class APIRouter(routing.Router):
|
||||||
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
responses: Dict[Union[int, str], Dict[str, Any]] = None,
|
||||||
deprecated: bool = None,
|
deprecated: bool = None,
|
||||||
operation_id: str = None,
|
operation_id: str = None,
|
||||||
|
response_model_include: Set[str] = None,
|
||||||
|
response_model_exclude: Set[str] = set(),
|
||||||
|
response_model_by_alias: bool = True,
|
||||||
response_model_skip_defaults: bool = False,
|
response_model_skip_defaults: bool = False,
|
||||||
include_in_schema: bool = True,
|
include_in_schema: bool = True,
|
||||||
response_class: Type[Response] = JSONResponse,
|
response_class: Type[Response] = JSONResponse,
|
||||||
|
|
@ -695,6 +790,9 @@ class APIRouter(routing.Router):
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
methods=["TRACE"],
|
methods=["TRACE"],
|
||||||
operation_id=operation_id,
|
operation_id=operation_id,
|
||||||
|
response_model_include=response_model_include,
|
||||||
|
response_model_exclude=response_model_exclude,
|
||||||
|
response_model_by_alias=response_model_by_alias,
|
||||||
response_model_skip_defaults=response_model_skip_defaults,
|
response_model_skip_defaults=response_model_skip_defaults,
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
response_class=response_class,
|
response_class=response_class,
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ nav:
|
||||||
- Path Operation Advanced Configuration: 'tutorial/path-operation-advanced-configuration.md'
|
- Path Operation Advanced Configuration: 'tutorial/path-operation-advanced-configuration.md'
|
||||||
- Additional Status Codes: 'tutorial/additional-status-codes.md'
|
- Additional Status Codes: 'tutorial/additional-status-codes.md'
|
||||||
- JSON compatible encoder: 'tutorial/encoder.md'
|
- JSON compatible encoder: 'tutorial/encoder.md'
|
||||||
|
- Body - updates: 'tutorial/body-updates.md'
|
||||||
- Return a Response directly: 'tutorial/response-directly.md'
|
- Return a Response directly: 'tutorial/response-directly.md'
|
||||||
- Custom Response Class: 'tutorial/custom-response.md'
|
- Custom Response Class: 'tutorial/custom-response.md'
|
||||||
- Additional Responses in OpenAPI: 'tutorial/additional-responses.md'
|
- Additional Responses in OpenAPI: 'tutorial/additional-responses.md'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from fastapi import APIRouter, FastAPI
|
||||||
|
|
||||||
|
method_names = ["get", "put", "post", "delete", "options", "head", "patch", "trace"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_signatures_consistency():
|
||||||
|
base_sig = inspect.signature(APIRouter.get)
|
||||||
|
for method_name in method_names:
|
||||||
|
router_method = getattr(APIRouter, method_name)
|
||||||
|
app_method = getattr(FastAPI, method_name)
|
||||||
|
router_sig = inspect.signature(router_method)
|
||||||
|
app_sig = inspect.signature(app_method)
|
||||||
|
param: inspect.Parameter
|
||||||
|
for key, param in base_sig.parameters.items():
|
||||||
|
router_param: inspect.Parameter = router_sig.parameters[key]
|
||||||
|
app_param: inspect.Parameter = app_sig.parameters[key]
|
||||||
|
assert param.annotation == router_param.annotation
|
||||||
|
assert param.annotation == app_param.annotation
|
||||||
|
assert param.default == router_param.default
|
||||||
|
assert param.default == app_param.default
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
from starlette.testclient import TestClient
|
||||||
|
|
||||||
|
from body_updates.tutorial001 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "Fast API", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/items/{item_id}": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"summary": "Read Item",
|
||||||
|
"operationId": "read_item_items__item_id__get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"required": True,
|
||||||
|
"schema": {"title": "Item_Id", "type": "string"},
|
||||||
|
"name": "item_id",
|
||||||
|
"in": "path",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"summary": "Update Item",
|
||||||
|
"operationId": "update_item_items__item_id__put",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"required": True,
|
||||||
|
"schema": {"title": "Item_Id", "type": "string"},
|
||||||
|
"name": "item_id",
|
||||||
|
"in": "path",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Item": {
|
||||||
|
"title": "Item",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {"title": "Name", "type": "string"},
|
||||||
|
"description": {"title": "Description", "type": "string"},
|
||||||
|
"price": {"title": "Price", "type": "number"},
|
||||||
|
"tax": {"title": "Tax", "type": "number", "default": 10.5},
|
||||||
|
"tags": {
|
||||||
|
"title": "Tags",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
"default": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_get():
|
||||||
|
response = client.get("/items/baz")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"name": "Baz",
|
||||||
|
"description": None,
|
||||||
|
"price": 50.2,
|
||||||
|
"tax": 10.5,
|
||||||
|
"tags": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_put():
|
||||||
|
response = client.put(
|
||||||
|
"/items/bar", json={"name": "Barz", "price": 3, "description": None}
|
||||||
|
)
|
||||||
|
assert response.json() == {
|
||||||
|
"name": "Barz",
|
||||||
|
"description": None,
|
||||||
|
"price": 3,
|
||||||
|
"tax": 10.5,
|
||||||
|
"tags": [],
|
||||||
|
}
|
||||||
|
|
@ -41,47 +41,7 @@ openapi_schema = {
|
||||||
"in": "path",
|
"in": "path",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
|
||||||
"patch": {
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Successful Response",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {"$ref": "#/components/schemas/Item"}
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
"422": {
|
|
||||||
"description": "Validation Error",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/HTTPValidationError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"summary": "Update Item",
|
|
||||||
"operationId": "update_item_items__item_id__patch",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"required": True,
|
|
||||||
"schema": {"title": "Item_Id", "type": "string"},
|
|
||||||
"name": "item_id",
|
|
||||||
"in": "path",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"requestBody": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {"$ref": "#/components/schemas/Item"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": True,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
|
@ -163,15 +123,3 @@ def test_get(url, data):
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json() == data
|
assert response.json() == data
|
||||||
|
|
||||||
|
|
||||||
def test_patch():
|
|
||||||
response = client.patch(
|
|
||||||
"/items/bar", json={"name": "Barz", "price": 3, "description": None}
|
|
||||||
)
|
|
||||||
assert response.json() == {
|
|
||||||
"name": "Barz",
|
|
||||||
"description": None,
|
|
||||||
"price": 3,
|
|
||||||
"tax": 20.2,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
from starlette.testclient import TestClient
|
||||||
|
|
||||||
|
from response_model.tutorial005 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "Fast API", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/items/{item_id}/name": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"summary": "Read Item Name",
|
||||||
|
"operationId": "read_item_name_items__item_id__name_get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"required": True,
|
||||||
|
"schema": {"title": "Item_Id", "type": "string"},
|
||||||
|
"name": "item_id",
|
||||||
|
"in": "path",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/items/{item_id}/public": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"summary": "Read Item Public Data",
|
||||||
|
"operationId": "read_item_public_data_items__item_id__public_get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"required": True,
|
||||||
|
"schema": {"title": "Item_Id", "type": "string"},
|
||||||
|
"name": "item_id",
|
||||||
|
"in": "path",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Item": {
|
||||||
|
"title": "Item",
|
||||||
|
"required": ["name", "price"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {"title": "Name", "type": "string"},
|
||||||
|
"price": {"title": "Price", "type": "number"},
|
||||||
|
"description": {"title": "Description", "type": "string"},
|
||||||
|
"tax": {"title": "Tax", "type": "number", "default": 10.5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_item_name():
|
||||||
|
response = client.get("/items/bar/name")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {"name": "Bar", "description": "The Bar fighters"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_item_public_data():
|
||||||
|
response = client.get("/items/bar/public")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"name": "Bar",
|
||||||
|
"description": "The Bar fighters",
|
||||||
|
"price": 62,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
from starlette.testclient import TestClient
|
||||||
|
|
||||||
|
from response_model.tutorial006 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "Fast API", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/items/{item_id}/name": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"summary": "Read Item Name",
|
||||||
|
"operationId": "read_item_name_items__item_id__name_get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"required": True,
|
||||||
|
"schema": {"title": "Item_Id", "type": "string"},
|
||||||
|
"name": "item_id",
|
||||||
|
"in": "path",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/items/{item_id}/public": {
|
||||||
|
"get": {
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"summary": "Read Item Public Data",
|
||||||
|
"operationId": "read_item_public_data_items__item_id__public_get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"required": True,
|
||||||
|
"schema": {"title": "Item_Id", "type": "string"},
|
||||||
|
"name": "item_id",
|
||||||
|
"in": "path",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Item": {
|
||||||
|
"title": "Item",
|
||||||
|
"required": ["name", "price"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {"title": "Name", "type": "string"},
|
||||||
|
"price": {"title": "Price", "type": "number"},
|
||||||
|
"description": {"title": "Description", "type": "string"},
|
||||||
|
"tax": {"title": "Tax", "type": "number", "default": 10.5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_item_name():
|
||||||
|
response = client.get("/items/bar/name")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {"name": "Bar", "description": "The Bar fighters"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_item_public_data():
|
||||||
|
response = client.get("/items/bar/public")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"name": "Bar",
|
||||||
|
"description": "The Bar fighters",
|
||||||
|
"price": 62,
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue