mirror of https://github.com/tiangolo/fastapi.git
Merge b419cf06a1 into 272204c0c7
This commit is contained in:
commit
743fbede20
|
|
@ -0,0 +1,29 @@
|
||||||
|
# HTTP QUERY { #http-query }
|
||||||
|
|
||||||
|
Typically, when you want to read data you use `GET`. If you need to send complex data (like a large JSON object) to filter that data, you traditionally had to use `POST` because `GET` requests does not support request bodies.
|
||||||
|
|
||||||
|
However, using `POST` for read-only operations isn't semantically correct, as `POST` implies that you are creating or modifying data.
|
||||||
|
|
||||||
|
There is a newer HTTP method called **QUERY**. It is designed exactly for this: performing safe, idempotent read operations that require a request body.
|
||||||
|
|
||||||
|
## Using `QUERY` { #using-query }
|
||||||
|
|
||||||
|
In **FastAPI**, you can use the `QUERY` method using the `@app.query()` decorator.
|
||||||
|
|
||||||
|
It works similarly to `@app.post()`, allowing you to receive Pydantic models in the body, but it signals that the operation is a read-only query.
|
||||||
|
|
||||||
|
|
||||||
|
{* ../../docs_src/http_query/tutorial001.py hl[7] *}
|
||||||
|
|
||||||
|
### Testing it { #testing-it }
|
||||||
|
|
||||||
|
You can test it using an HTTP client that supports the `QUERY` method.
|
||||||
|
|
||||||
|
Because it allows a body, you can send complex filters without hitting URL length limits common with `GET` query parameters.
|
||||||
|
|
||||||
|
### Technical Details { #technical-details }
|
||||||
|
|
||||||
|
The `QUERY` method is defined in the [IETF HTTP QUERY Method Draft](https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html). It is considered:
|
||||||
|
|
||||||
|
* **Safe**: It does not alter the state of the server (read-only).
|
||||||
|
* **Idempotent**: Making the same request multiple times yields the same result.
|
||||||
|
|
@ -135,6 +135,7 @@ nav:
|
||||||
- tutorial/request-forms-and-files.md
|
- tutorial/request-forms-and-files.md
|
||||||
- tutorial/handling-errors.md
|
- tutorial/handling-errors.md
|
||||||
- tutorial/path-operation-configuration.md
|
- tutorial/path-operation-configuration.md
|
||||||
|
- tutorial/http-query.md
|
||||||
- tutorial/encoder.md
|
- tutorial/encoder.md
|
||||||
- tutorial/body-updates.md
|
- tutorial/body-updates.md
|
||||||
- Dependencies:
|
- Dependencies:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class ItemSearch(BaseModel):
|
||||||
|
keyword: str
|
||||||
|
min_price: Optional[float] = None
|
||||||
|
max_price: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
|
@app.query("/items/")
|
||||||
|
async def search_items(search_params: ItemSearch):
|
||||||
|
return {"message": "Searching items", "search_params": search_params}
|
||||||
|
|
@ -4541,6 +4541,384 @@ class FastAPI(Starlette):
|
||||||
generate_unique_id_function=generate_unique_id_function,
|
generate_unique_id_function=generate_unique_id_function,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def query(
|
||||||
|
self,
|
||||||
|
path: Annotated[
|
||||||
|
str,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The URL path to be used for this *path operation*.
|
||||||
|
|
||||||
|
For example, in `http://example.com/items`, the path is `/items`.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
],
|
||||||
|
*,
|
||||||
|
response_model: Annotated[
|
||||||
|
Any,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The type to use for the response.
|
||||||
|
|
||||||
|
It could be any valid Pydantic *field* type. So, it doesn't have to
|
||||||
|
be a Pydantic model, it could be other things, like a `list`, `dict`,
|
||||||
|
etc.
|
||||||
|
|
||||||
|
It will be used for:
|
||||||
|
|
||||||
|
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
|
||||||
|
show it as the response (JSON Schema).
|
||||||
|
* Serialization: you could return an arbitrary object and the
|
||||||
|
`response_model` would be used to serialize that object into the
|
||||||
|
corresponding JSON.
|
||||||
|
* Filtering: the JSON sent to the client will only contain the data
|
||||||
|
(fields) defined in the `response_model`. If you returned an object
|
||||||
|
that contains an attribute `password` but the `response_model` does
|
||||||
|
not include that field, the JSON sent to the client would not have
|
||||||
|
that `password`.
|
||||||
|
* Validation: whatever you return will be serialized with the
|
||||||
|
`response_model`, converting any data as necessary to generate the
|
||||||
|
corresponding JSON. But if the data in the object returned is not
|
||||||
|
valid, that would mean a violation of the contract with the client,
|
||||||
|
so it's an error from the API developer. So, FastAPI will raise an
|
||||||
|
error and return a 500 error code (Internal Server Error).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = Default(None),
|
||||||
|
status_code: Annotated[
|
||||||
|
Optional[int],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The default status code to be used for the response.
|
||||||
|
|
||||||
|
You could override the status code by returning a response directly.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
tags: Annotated[
|
||||||
|
Optional[List[Union[str, Enum]]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A list of tags to be applied to the *path operation*.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
dependencies: Annotated[
|
||||||
|
Optional[Sequence[Depends]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A list of dependencies (using `Depends()`) to be applied to the
|
||||||
|
*path operation*.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
summary: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A summary for the *path operation*.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
description: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A description for the *path operation*.
|
||||||
|
|
||||||
|
If not provided, it will be extracted automatically from the docstring
|
||||||
|
of the *path operation function*.
|
||||||
|
|
||||||
|
It can contain Markdown.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_description: Annotated[
|
||||||
|
str,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The description for the default response.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = "Successful Response",
|
||||||
|
responses: Annotated[
|
||||||
|
Optional[Dict[Union[int, str], Dict[str, Any]]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Additional responses that could be returned by this *path operation*.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
deprecated: Annotated[
|
||||||
|
Optional[bool],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Mark this *path operation* as deprecated.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
operation_id: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Custom operation ID to be used by this *path operation*.
|
||||||
|
|
||||||
|
By default, it is generated automatically.
|
||||||
|
|
||||||
|
If you provide a custom operation ID, you need to make sure it is
|
||||||
|
unique for the whole API.
|
||||||
|
|
||||||
|
You can customize the
|
||||||
|
operation ID generation with the parameter
|
||||||
|
`generate_unique_id_function` in the `FastAPI` class.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_model_include: Annotated[
|
||||||
|
Optional[IncEx],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to include only certain fields in the
|
||||||
|
response data.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_model_exclude: Annotated[
|
||||||
|
Optional[IncEx],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to exclude certain fields in the
|
||||||
|
response data.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_model_by_alias: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response model
|
||||||
|
should be serialized by alias when an alias is used.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = True,
|
||||||
|
response_model_exclude_unset: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response data
|
||||||
|
should have all the fields, including the ones that were not set and
|
||||||
|
have their default values. This is different from
|
||||||
|
`response_model_exclude_defaults` in that if the fields are set,
|
||||||
|
they will be included in the response, even if the value is the same
|
||||||
|
as the default.
|
||||||
|
|
||||||
|
When `True`, default values are omitted from the response.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
response_model_exclude_defaults: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response data
|
||||||
|
should have all the fields, including the ones that have the same value
|
||||||
|
as the default. This is different from `response_model_exclude_unset`
|
||||||
|
in that if the fields are set but contain the same default values,
|
||||||
|
they will be excluded from the response.
|
||||||
|
|
||||||
|
When `True`, default values are omitted from the response.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
response_model_exclude_none: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response data should
|
||||||
|
exclude fields set to `None`.
|
||||||
|
|
||||||
|
This is much simpler (less smart) than `response_model_exclude_unset`
|
||||||
|
and `response_model_exclude_defaults`. You probably want to use one of
|
||||||
|
those two instead of this one, as those allow returning `None` values
|
||||||
|
when it makes sense.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
include_in_schema: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Include this *path operation* in the generated OpenAPI schema.
|
||||||
|
|
||||||
|
This affects the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = True,
|
||||||
|
response_class: Annotated[
|
||||||
|
Type[Response],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Response class to be used for this *path operation*.
|
||||||
|
|
||||||
|
This will not be used if you return a response directly.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = Default(JSONResponse),
|
||||||
|
name: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Name for this *path operation*. Only used internally.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
callbacks: Annotated[
|
||||||
|
Optional[List[BaseRoute]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
List of *path operations* that will be used as OpenAPI callbacks.
|
||||||
|
|
||||||
|
This is only for OpenAPI documentation, the callbacks won't be used
|
||||||
|
directly.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
openapi_extra: Annotated[
|
||||||
|
Optional[Dict[str, Any]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Extra metadata to be included in the OpenAPI schema for this *path
|
||||||
|
operation*.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
generate_unique_id_function: Annotated[
|
||||||
|
Callable[[routing.APIRoute], str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Customize the function used to generate unique IDs for the *path
|
||||||
|
operations* shown in the generated OpenAPI.
|
||||||
|
|
||||||
|
This is particularly useful when automatically generating clients or
|
||||||
|
SDKs for your API.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = Default(generate_unique_id),
|
||||||
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
||||||
|
"""
|
||||||
|
Add a *path operation* using an HTTP QUERY operation.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: str | None = None
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.query("/items/")
|
||||||
|
def query_item(item: Item):
|
||||||
|
return {"message": "Item queried", "item": item}
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
return self.router.query(
|
||||||
|
path,
|
||||||
|
response_model=response_model,
|
||||||
|
status_code=status_code,
|
||||||
|
tags=tags,
|
||||||
|
dependencies=dependencies,
|
||||||
|
summary=summary,
|
||||||
|
description=description,
|
||||||
|
response_description=response_description,
|
||||||
|
responses=responses,
|
||||||
|
deprecated=deprecated,
|
||||||
|
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_exclude_unset=response_model_exclude_unset,
|
||||||
|
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||||
|
response_model_exclude_none=response_model_exclude_none,
|
||||||
|
include_in_schema=include_in_schema,
|
||||||
|
response_class=response_class,
|
||||||
|
name=name,
|
||||||
|
callbacks=callbacks,
|
||||||
|
openapi_extra=openapi_extra,
|
||||||
|
generate_unique_id_function=generate_unique_id_function,
|
||||||
|
)
|
||||||
|
|
||||||
def websocket_route(
|
def websocket_route(
|
||||||
self, path: str, name: Union[str, None] = None
|
self, path: str, name: Union[str, None] = None
|
||||||
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
METHODS_WITH_BODY = {"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"}
|
METHODS_WITH_BODY = {"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "QUERY"}
|
||||||
REF_PREFIX = "#/components/schemas/"
|
REF_PREFIX = "#/components/schemas/"
|
||||||
REF_TEMPLATE = "#/components/schemas/{model}"
|
REF_TEMPLATE = "#/components/schemas/{model}"
|
||||||
|
|
|
||||||
|
|
@ -4538,6 +4538,388 @@ class APIRouter(routing.Router):
|
||||||
generate_unique_id_function=generate_unique_id_function,
|
generate_unique_id_function=generate_unique_id_function,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def query(
|
||||||
|
self,
|
||||||
|
path: Annotated[
|
||||||
|
str,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The URL path to be used for this *path operation*.
|
||||||
|
|
||||||
|
For example, in `http://example.com/items`, the path is `/items`.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
],
|
||||||
|
*,
|
||||||
|
response_model: Annotated[
|
||||||
|
Any,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The type to use for the response.
|
||||||
|
|
||||||
|
It could be any valid Pydantic *field* type. So, it doesn't have to
|
||||||
|
be a Pydantic model, it could be other things, like a `list`, `dict`,
|
||||||
|
etc.
|
||||||
|
|
||||||
|
It will be used for:
|
||||||
|
|
||||||
|
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
|
||||||
|
show it as the response (JSON Schema).
|
||||||
|
* Serialization: you could return an arbitrary object and the
|
||||||
|
`response_model` would be used to serialize that object into the
|
||||||
|
corresponding JSON.
|
||||||
|
* Filtering: the JSON sent to the client will only contain the data
|
||||||
|
(fields) defined in the `response_model`. If you returned an object
|
||||||
|
that contains an attribute `password` but the `response_model` does
|
||||||
|
not include that field, the JSON sent to the client would not have
|
||||||
|
that `password`.
|
||||||
|
* Validation: whatever you return will be serialized with the
|
||||||
|
`response_model`, converting any data as necessary to generate the
|
||||||
|
corresponding JSON. But if the data in the object returned is not
|
||||||
|
valid, that would mean a violation of the contract with the client,
|
||||||
|
so it's an error from the API developer. So, FastAPI will raise an
|
||||||
|
error and return a 500 error code (Internal Server Error).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = Default(None),
|
||||||
|
status_code: Annotated[
|
||||||
|
Optional[int],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The default status code to be used for the response.
|
||||||
|
|
||||||
|
You could override the status code by returning a response directly.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
tags: Annotated[
|
||||||
|
Optional[List[Union[str, Enum]]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A list of tags to be applied to the *path operation*.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
dependencies: Annotated[
|
||||||
|
Optional[Sequence[params.Depends]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A list of dependencies (using `Depends()`) to be applied to the
|
||||||
|
*path operation*.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
summary: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A summary for the *path operation*.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
description: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
A description for the *path operation*.
|
||||||
|
|
||||||
|
If not provided, it will be extracted automatically from the docstring
|
||||||
|
of the *path operation function*.
|
||||||
|
|
||||||
|
It can contain Markdown.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_description: Annotated[
|
||||||
|
str,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
The description for the default response.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = "Successful Response",
|
||||||
|
responses: Annotated[
|
||||||
|
Optional[Dict[Union[int, str], Dict[str, Any]]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Additional responses that could be returned by this *path operation*.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
deprecated: Annotated[
|
||||||
|
Optional[bool],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Mark this *path operation* as deprecated.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
operation_id: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Custom operation ID to be used by this *path operation*.
|
||||||
|
|
||||||
|
By default, it is generated automatically.
|
||||||
|
|
||||||
|
If you provide a custom operation ID, you need to make sure it is
|
||||||
|
unique for the whole API.
|
||||||
|
|
||||||
|
You can customize the
|
||||||
|
operation ID generation with the parameter
|
||||||
|
`generate_unique_id_function` in the `FastAPI` class.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_model_include: Annotated[
|
||||||
|
Optional[IncEx],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to include only certain fields in the
|
||||||
|
response data.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_model_exclude: Annotated[
|
||||||
|
Optional[IncEx],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to exclude certain fields in the
|
||||||
|
response data.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
response_model_by_alias: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response model
|
||||||
|
should be serialized by alias when an alias is used.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = True,
|
||||||
|
response_model_exclude_unset: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response data
|
||||||
|
should have all the fields, including the ones that were not set and
|
||||||
|
have their default values. This is different from
|
||||||
|
`response_model_exclude_defaults` in that if the fields are set,
|
||||||
|
they will be included in the response, even if the value is the same
|
||||||
|
as the default.
|
||||||
|
|
||||||
|
When `True`, default values are omitted from the response.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
response_model_exclude_defaults: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response data
|
||||||
|
should have all the fields, including the ones that have the same value
|
||||||
|
as the default. This is different from `response_model_exclude_unset`
|
||||||
|
in that if the fields are set but contain the same default values,
|
||||||
|
they will be excluded from the response.
|
||||||
|
|
||||||
|
When `True`, default values are omitted from the response.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
response_model_exclude_none: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Configuration passed to Pydantic to define if the response data should
|
||||||
|
exclude fields set to `None`.
|
||||||
|
|
||||||
|
This is much simpler (less smart) than `response_model_exclude_unset`
|
||||||
|
and `response_model_exclude_defaults`. You probably want to use one of
|
||||||
|
those two instead of this one, as those allow returning `None` values
|
||||||
|
when it makes sense.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
include_in_schema: Annotated[
|
||||||
|
bool,
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Include this *path operation* in the generated OpenAPI schema.
|
||||||
|
|
||||||
|
This affects the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = True,
|
||||||
|
response_class: Annotated[
|
||||||
|
Type[Response],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Response class to be used for this *path operation*.
|
||||||
|
|
||||||
|
This will not be used if you return a response directly.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = Default(JSONResponse),
|
||||||
|
name: Annotated[
|
||||||
|
Optional[str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Name for this *path operation*. Only used internally.
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
callbacks: Annotated[
|
||||||
|
Optional[List[BaseRoute]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
List of *path operations* that will be used as OpenAPI callbacks.
|
||||||
|
|
||||||
|
This is only for OpenAPI documentation, the callbacks won't be used
|
||||||
|
directly.
|
||||||
|
|
||||||
|
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
openapi_extra: Annotated[
|
||||||
|
Optional[Dict[str, Any]],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Extra metadata to be included in the OpenAPI schema for this *path
|
||||||
|
operation*.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = None,
|
||||||
|
generate_unique_id_function: Annotated[
|
||||||
|
Callable[[APIRoute], str],
|
||||||
|
Doc(
|
||||||
|
"""
|
||||||
|
Customize the function used to generate unique IDs for the *path
|
||||||
|
operations* shown in the generated OpenAPI.
|
||||||
|
|
||||||
|
This is particularly useful when automatically generating clients or
|
||||||
|
SDKs for your API.
|
||||||
|
|
||||||
|
Read more about it in the
|
||||||
|
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
] = Default(generate_unique_id),
|
||||||
|
) -> Callable[[DecoratedCallable], DecoratedCallable]:
|
||||||
|
"""
|
||||||
|
Add a *path operation* using an HTTP QUERY operation.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import APIRouter, FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: str | None = None
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.query("/items/")
|
||||||
|
def query_items(item: Item):
|
||||||
|
return {"message": "Items queried", "query": item}
|
||||||
|
|
||||||
|
app.include_router(router)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
return self.api_route(
|
||||||
|
path=path,
|
||||||
|
response_model=response_model,
|
||||||
|
status_code=status_code,
|
||||||
|
tags=tags,
|
||||||
|
dependencies=dependencies,
|
||||||
|
summary=summary,
|
||||||
|
description=description,
|
||||||
|
response_description=response_description,
|
||||||
|
responses=responses,
|
||||||
|
deprecated=deprecated,
|
||||||
|
methods=["QUERY"],
|
||||||
|
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_exclude_unset=response_model_exclude_unset,
|
||||||
|
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||||
|
response_model_exclude_none=response_model_exclude_none,
|
||||||
|
include_in_schema=include_in_schema,
|
||||||
|
response_class=response_class,
|
||||||
|
name=name,
|
||||||
|
callbacks=callbacks,
|
||||||
|
openapi_extra=openapi_extra,
|
||||||
|
generate_unique_id_function=generate_unique_id_function,
|
||||||
|
)
|
||||||
|
|
||||||
@deprecated(
|
@deprecated(
|
||||||
"""
|
"""
|
||||||
on_event is deprecated, use lifespan event handlers instead.
|
on_event is deprecated, use lifespan event handlers instead.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from dirty_equals import IsDict
|
||||||
|
from fastapi import APIRouter, FastAPI
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
price: float
|
||||||
|
tax: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
|
@app.query("/items/")
|
||||||
|
def query_item(item: Item):
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
@app.query("/items/{item_id}")
|
||||||
|
def query_item_by_id(item_id: int, item: Item, q: Optional[str] = None):
|
||||||
|
return {"item_id": item_id, "item": item, "q": q}
|
||||||
|
|
||||||
|
|
||||||
|
@router.query("/router-items/")
|
||||||
|
def router_query_item(item: Item):
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
app.include_router(router)
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_item():
|
||||||
|
response = client.request("QUERY", "/items/", json={"name": "Foo", "price": 50.5})
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"name": "Foo",
|
||||||
|
"price": 50.5,
|
||||||
|
"description": None,
|
||||||
|
"tax": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_item_validation_error():
|
||||||
|
response = client.request("QUERY", "/items/", json={"name": "Foo"})
|
||||||
|
assert response.status_code == 422
|
||||||
|
assert response.json() == IsDict(
|
||||||
|
{
|
||||||
|
"detail": [
|
||||||
|
{
|
||||||
|
"type": "missing",
|
||||||
|
"loc": ["body", "price"],
|
||||||
|
"msg": "Field required",
|
||||||
|
"input": {"name": "Foo"},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
) | IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{
|
||||||
|
"detail": [
|
||||||
|
{
|
||||||
|
"loc": ["body", "price"],
|
||||||
|
"msg": "field required",
|
||||||
|
"type": "value_error.missing",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_item_by_id():
|
||||||
|
response = client.request(
|
||||||
|
"QUERY",
|
||||||
|
"/items/42?q=somequery",
|
||||||
|
json={"name": "Bar", "price": 10.0},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"item_id": 42,
|
||||||
|
"item": {"name": "Bar", "price": 10.0, "description": None, "tax": None},
|
||||||
|
"q": "somequery",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_router_query_item():
|
||||||
|
response = client.request(
|
||||||
|
"QUERY", "/router-items/", json={"name": "Baz", "price": 20.0}
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"name": "Baz",
|
||||||
|
"price": 20.0,
|
||||||
|
"description": None,
|
||||||
|
"tax": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/items/": {
|
||||||
|
"query": {
|
||||||
|
"summary": "Query Item",
|
||||||
|
"operationId": "query_item_items__query",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/items/{item_id}": {
|
||||||
|
"query": {
|
||||||
|
"summary": "Query Item By Id",
|
||||||
|
"operationId": "query_item_by_id_items__item_id__query",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"required": True,
|
||||||
|
"schema": {"title": "Item Id", "type": "integer"},
|
||||||
|
"name": "item_id",
|
||||||
|
"in": "path",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"required": False,
|
||||||
|
"schema": IsDict(
|
||||||
|
{
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
"title": "Q",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Q", "type": "string"}
|
||||||
|
),
|
||||||
|
"name": "q",
|
||||||
|
"in": "query",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/router-items/": {
|
||||||
|
"query": {
|
||||||
|
"summary": "Router Query Item",
|
||||||
|
"operationId": "router_query_item_router_items__query",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {"$ref": "#/components/schemas/Item"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Item": {
|
||||||
|
"title": "Item",
|
||||||
|
"required": ["name", "price"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {"title": "Name", "type": "string"},
|
||||||
|
"description": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Description",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Description", "type": "string"}
|
||||||
|
),
|
||||||
|
"price": {"title": "Price", "type": "number"},
|
||||||
|
"tax": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Tax",
|
||||||
|
"anyOf": [{"type": "number"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Tax", "type": "number"}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from docs_src.http_query.tutorial001 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_query_items():
|
||||||
|
response = client.request(
|
||||||
|
"QUERY",
|
||||||
|
"/items/",
|
||||||
|
json={"keyword": "book", "max_price": 50},
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {
|
||||||
|
"message": "Searching items",
|
||||||
|
"search_params": {
|
||||||
|
"keyword": "book",
|
||||||
|
"min_price": None,
|
||||||
|
"max_price": 50.0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200
|
||||||
|
schema = response.json()
|
||||||
|
assert "query" in schema["paths"]["/items/"]
|
||||||
|
operation = schema["paths"]["/items/"]["query"]
|
||||||
|
assert "requestBody" in operation
|
||||||
|
assert (
|
||||||
|
"ItemSearch"
|
||||||
|
in operation["requestBody"]["content"]["application/json"]["schema"]["$ref"]
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue