mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-26 07:40:57 -05:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e9d5aacf8 | ||
|
|
d27a218bc3 | ||
|
|
0c4ded88fe | ||
|
|
0e0931d308 | ||
|
|
dcfa9eb8fe | ||
|
|
6ebf60b175 | ||
|
|
a4d0724a97 | ||
|
|
c235e9b78c | ||
|
|
5fa3e239db | ||
|
|
3d1b107d70 | ||
|
|
18e0828daf | ||
|
|
f16c7729bd | ||
|
|
9f3c8cd139 | ||
|
|
a7a35aee61 | ||
|
|
dc5a966548 | ||
|
|
ea8d7f689e | ||
|
|
0ed6c92341 |
@@ -161,10 +161,33 @@ An alternative JSON response using <a href="https://github.com/ultrajson/ultrajs
|
||||
|
||||
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
|
||||
|
||||
You can return a `RedirectResponse` directly:
|
||||
|
||||
```Python hl_lines="2 9"
|
||||
{!../../../docs_src/custom_response/tutorial006.py!}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Or you can use it in the `response_class` parameter:
|
||||
|
||||
|
||||
```Python hl_lines="2 7 9"
|
||||
{!../../../docs_src/custom_response/tutorial006b.py!}
|
||||
```
|
||||
|
||||
If you do that, then you can return the URL directly from your *path operation* function.
|
||||
|
||||
In this case, the `status_code` used will be the default one for the `RedirectResponse`, which is `307`.
|
||||
|
||||
---
|
||||
|
||||
You can also use the `status_code` parameter combined with the `response_class` parameter:
|
||||
|
||||
```Python hl_lines="2 7 9"
|
||||
{!../../../docs_src/custom_response/tutorial006c.py!}
|
||||
```
|
||||
|
||||
### `StreamingResponse`
|
||||
|
||||
Takes an async generator or a normal generator/iterator and streams the response body.
|
||||
@@ -203,6 +226,14 @@ File responses will include appropriate `Content-Length`, `Last-Modified` and `E
|
||||
{!../../../docs_src/custom_response/tutorial009.py!}
|
||||
```
|
||||
|
||||
You can also use the `response_class` parameter:
|
||||
|
||||
```Python hl_lines="2 8 10"
|
||||
{!../../../docs_src/custom_response/tutorial009b.py!}
|
||||
```
|
||||
|
||||
In this case, you can return the file path directly from your *path operation* function.
|
||||
|
||||
## Default response class
|
||||
|
||||
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
## Latest Changes
|
||||
|
||||
|
||||
## 0.66.0
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Allow setting the `response_class` to `RedirectResponse` and returning the URL from the function. New and updated docs are in the tutorial section **Custom Response - HTML, Stream, File, others**, in [RedirectResponse](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse) and in [FileResponse](https://fastapi.tiangolo.com/advanced/custom-response/#fileresponse). PR [#3457](https://github.com/tiangolo/fastapi/pull/3457) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Fix include/exclude for dicts in `jsonable_encoder`. PR [#2016](https://github.com/tiangolo/fastapi/pull/2016) by [@Rubikoid](https://github.com/Rubikoid).
|
||||
* 🐛 Support custom OpenAPI / JSON Schema fields in the generated output OpenAPI. PR [#1429](https://github.com/tiangolo/fastapi/pull/1429) by [@jmagnusson](https://github.com/jmagnusson).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Spanish translation for `tutorial/query-params.md`. PR [#2243](https://github.com/tiangolo/fastapi/pull/2243) by [@mariacamilagl](https://github.com/mariacamilagl).
|
||||
* 🌐 Add Spanish translation for `advanced/response-directly.md`. PR [#1253](https://github.com/tiangolo/fastapi/pull/1253) by [@jfunez](https://github.com/jfunez).
|
||||
* 🌐 Add Spanish translation for `advanced/additional-status-codes.md`. PR [#1252](https://github.com/tiangolo/fastapi/pull/1252) by [@jfunez](https://github.com/jfunez).
|
||||
* 🌐 Add Spanish translation for `advanced/path-operation-advanced-configuration.md`. PR [#1251](https://github.com/tiangolo/fastapi/pull/1251) by [@jfunez](https://github.com/jfunez).
|
||||
|
||||
## 0.65.3
|
||||
|
||||
### Fixes
|
||||
|
||||
37
docs/es/docs/advanced/additional-status-codes.md
Normal file
37
docs/es/docs/advanced/additional-status-codes.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Códigos de estado adicionales
|
||||
|
||||
Por defecto, **FastAPI** devolverá las respuestas utilizando una `JSONResponse`, poniendo el contenido que devuelves en tu *operación de path* dentro de esa `JSONResponse`.
|
||||
|
||||
Utilizará el código de estado por defecto, o el que hayas asignado en tu *operación de path*.
|
||||
|
||||
## Códigos de estado adicionales
|
||||
|
||||
Si quieres devolver códigos de estado adicionales además del principal, puedes hacerlo devolviendo directamente una `Response`, como una `JSONResponse`, y asignar directamente el código de estado adicional.
|
||||
|
||||
Por ejemplo, digamos que quieres tener una *operación de path* que permita actualizar ítems y devolver códigos de estado HTTP 200 "OK" cuando sea exitosa.
|
||||
|
||||
Pero también quieres que acepte nuevos ítems. Cuando los ítems no existan anteriormente, serán creados y devolverá un código de estado HTTP 201 "Created".
|
||||
|
||||
Para conseguir esto importa `JSONResponse` y devuelve ahí directamente tu contenido, asignando el `status_code` que quieras:
|
||||
|
||||
```Python hl_lines="2 19"
|
||||
{!../../../docs_src/additional_status_codes/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! warning "Advertencia"
|
||||
Cuando devuelves directamente una `Response`, como en los ejemplos anteriores, será devuelta directamente.
|
||||
|
||||
No será serializado con el modelo, etc.
|
||||
|
||||
Asegurate de que la respuesta tenga los datos que quieras, y que los valores sean JSON válidos (si estás usando `JSONResponse`).
|
||||
|
||||
!!! note "Detalles Técnicos"
|
||||
También podrías utilizar `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** provee las mismas `starlette.responses` que `fastapi.responses` simplemente como una convención para ti, el desarrollador. Pero la mayoría de las respuestas disponibles vienen directamente de Starlette. Lo mismo con `status`.
|
||||
|
||||
## OpenAPI y documentación de API
|
||||
|
||||
Si quieres devolver códigos de estado y respuestas adicionales directamente, estas no estarán incluidas en el schema de OpenAPI (documentación de API), porque FastAPI no tiene una manera de conocer de antemano lo que vas a devolver.
|
||||
|
||||
Pero puedes documentar eso en tu código usando [Respuestas Adicionales](additional-responses.md){.internal-link target=_blank}.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Configuración avanzada de las operaciones de path
|
||||
|
||||
## OpenAPI operationId
|
||||
|
||||
!!! warning "Advertencia"
|
||||
Si no eres una persona "experta" en OpenAPI, probablemente no necesitas leer esto.
|
||||
|
||||
Puedes asignar el `operationId` de OpenAPI para ser usado en tu *operación de path* con el parámetro `operation_id`.
|
||||
|
||||
En este caso tendrías que asegurarte de que sea único para cada operación.
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Usando el nombre de la *función de la operación de path* en el operationId
|
||||
|
||||
Si quieres usar tus nombres de funciones de API como `operationId`s, puedes iterar sobre todos ellos y sobrescribir `operation_id` de cada *operación de path* usando su `APIRoute.name`.
|
||||
|
||||
Deberías hacerlo después de adicionar todas tus *operaciones de path*.
|
||||
|
||||
```Python hl_lines="2 12 13 14 15 16 17 18 19 20 21 24"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! tip "Consejo"
|
||||
Si llamas manualmente a `app.openapi()`, debes actualizar el `operationId`s antes de hacerlo.
|
||||
|
||||
!!! warning "Advertencia"
|
||||
Si haces esto, debes asegurarte de que cada una de tus *funciones de las operaciones de path* tenga un nombre único.
|
||||
|
||||
Incluso si están en diferentes módulos (archivos Python).
|
||||
|
||||
## Excluir de OpenAPI
|
||||
|
||||
Para excluir una *operación de path* del esquema OpenAPI generado (y por tanto del la documentación generada automáticamente), usa el parámetro `include_in_schema` y asigna el valor como `False`;
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
## Descripción avanzada desde el docstring
|
||||
|
||||
Puedes limitar las líneas usadas desde el docstring de una *operación de path* para OpenAPI.
|
||||
|
||||
Agregar un `\f` (un carácter de "form feed" escapado) hace que **FastAPI** trunque el output utilizada para OpenAPI en ese punto.
|
||||
|
||||
No será mostrado en la documentación, pero otras herramientas (como Sphinx) serán capaces de usar el resto.
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
|
||||
```
|
||||
63
docs/es/docs/advanced/response-directly.md
Normal file
63
docs/es/docs/advanced/response-directly.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Devolver una respuesta directamente
|
||||
|
||||
Cuando creas una *operación de path* normalmente puedes devolver cualquier dato: un `dict`, una `list`, un modelo Pydantic, un modelo de base de datos, etc.
|
||||
|
||||
Por defecto, **FastAPI** convertiría automáticamente ese valor devuelto a JSON usando el `jsonable_encoder` explicado en [Codificador Compatible JSON](../tutorial/encoder.md){.internal-link target=_blank}.
|
||||
|
||||
Luego, tras bastidores, pondría esos datos compatibles con JSON (por ejemplo, un `dict`) dentro de una `JSONResponse` que se usaría para enviar la respuesta al cliente.
|
||||
|
||||
Pero puedes devolver una `JSONResponse` directamente de tu *operación de path*.
|
||||
|
||||
Esto puede ser útil, por ejemplo, para devolver cookies o headers personalizados.
|
||||
|
||||
## Devolver una `Response`
|
||||
|
||||
De hecho, puedes devolver cualquier `Response` o cualquier subclase de la misma.
|
||||
|
||||
!!! tip "Consejo"
|
||||
`JSONResponse` en sí misma es una subclase de `Response`.
|
||||
|
||||
Y cuando devuelves una `Response`, **FastAPI** la pasará directamente.
|
||||
|
||||
No hará ninguna conversión de datos con modelos Pydantic, no convertirá el contenido a ningún tipo, etc.
|
||||
|
||||
Esto te da mucha flexibilidad. Puedes devolver cualquier tipo de dato, sobrescribir cualquer declaración de datos o validación, etc.
|
||||
|
||||
## Usando el `jsonable_encoder` en una `Response`
|
||||
|
||||
Como **FastAPI** no realiza ningún cambio en la `Response` que devuelves, debes asegurarte de que el contenido está listo.
|
||||
|
||||
Por ejemplo, no puedes poner un modelo Pydantic en una `JSONResponse` sin primero convertirlo a un `dict` con todos los tipos de datos (como `datetime`, `UUID`, etc) convertidos a tipos compatibles con JSON.
|
||||
|
||||
Para esos casos, puedes usar el `jsonable_encoder` para convertir tus datos antes de pasarlos a la respuesta:
|
||||
|
||||
```Python hl_lines="4 6 20 21"
|
||||
{!../../../docs_src/response_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Detalles Técnicos"
|
||||
También puedes usar `from starlette.responses import JSONResponse`.
|
||||
|
||||
**FastAPI** provee `starlette.responses` como `fastapi.responses`, simplemente como una conveniencia para ti, el desarrollador. Pero la mayoría de las respuestas disponibles vienen directamente de Starlette.
|
||||
|
||||
## Devolviendo una `Response` personalizada
|
||||
|
||||
El ejemplo anterior muestra las partes que necesitas, pero no es muy útil todavía, dado que podrías simplemente devolver el `item` directamente, y **FastAPI** lo pondría en una `JSONResponse` por ti, convirtiéndolo en un `dict`, etc. Todo esto por defecto.
|
||||
|
||||
Ahora, veamos cómo puedes usarlo para devolver una respuesta personalizada.
|
||||
|
||||
Digamos que quieres devolver una respuesta <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>.
|
||||
|
||||
Podrías poner tu contenido XML en un string, ponerlo en una `Response` y devolverlo:
|
||||
|
||||
```Python hl_lines="1 18"
|
||||
{!../../../docs_src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Cuando devuelves una `Response` directamente, los datos no son validados, convertidos (serializados), ni documentados automáticamente.
|
||||
|
||||
Pero todavía es posible documentarlo como es descrito en [Respuestas adicionales en OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
Puedes ver en secciones posteriores como usar/declarar esas `Response`s personalizadas aún teniendo conversión automática de datos, documentación, etc.
|
||||
197
docs/es/docs/tutorial/query-params.md
Normal file
197
docs/es/docs/tutorial/query-params.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# Parámetros de query
|
||||
|
||||
Cuando declaras otros parámetros de la función que no hacen parte de los parámetros de path estos se interpretan automáticamente como parámetros de "query".
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
El query es el conjunto de pares de key-value que van después del `?` en la URL, separados por caracteres `&`.
|
||||
|
||||
Por ejemplo, en la URL:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
```
|
||||
|
||||
...los parámetros de query son:
|
||||
|
||||
* `skip`: con un valor de `0`
|
||||
* `limit`: con un valor de `10`
|
||||
|
||||
Dado que son parte de la URL son strings "naturalmente".
|
||||
|
||||
Pero cuando los declaras con tipos de Python (en el ejemplo arriba, como `int`) son convertidos a ese tipo y son validados con él.
|
||||
|
||||
Todo el proceso que aplicaba a los parámetros de path también aplica a los parámetros de query:
|
||||
|
||||
* Soporte del editor (obviamente)
|
||||
* <abbr title="convertir el string que viene de un HTTP request a datos de Python">"Parsing"</abbr> de datos
|
||||
* Validación de datos
|
||||
* Documentación automática
|
||||
|
||||
## Configuraciones por defecto
|
||||
|
||||
Como los parámetros de query no están fijos en una parte del path pueden ser opcionales y pueden tener valores por defecto.
|
||||
|
||||
El ejemplo arriba tiene `skip=0` y `limit=10` como los valores por defecto.
|
||||
|
||||
Entonces, si vas a la URL:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/
|
||||
```
|
||||
|
||||
Sería lo mismo que ir a:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=0&limit=10
|
||||
```
|
||||
|
||||
Pero, si por ejemplo vas a:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/?skip=20
|
||||
```
|
||||
|
||||
Los valores de los parámetros en tu función serán:
|
||||
|
||||
* `skip=20`: porque lo definiste en la URL
|
||||
* `limit=10`: porque era el valor por defecto
|
||||
|
||||
## Parámetros opcionales
|
||||
|
||||
Del mismo modo puedes declarar parámetros de query opcionales definiendo el valor por defecto como `None`:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
En este caso el parámetro de la función `q` será opcional y será `None` por defecto.
|
||||
|
||||
!!! check "Revisa"
|
||||
También puedes notar que **FastAPI** es lo suficientemente inteligente para darse cuenta de que el parámetro de path `item_id` es un parámetro de path y que `q` no lo es, y por lo tanto es un parámetro de query.
|
||||
|
||||
!!! note "Nota"
|
||||
FastAPI sabrá que `q` es opcional por el `= None`.
|
||||
|
||||
El `Optional` en `Optional[str]` no es usado por FastAPI (FastAPI solo usará la parte `str`), pero el `Optional[str]` le permitirá a tu editor ayudarte a encontrar errores en tu código.
|
||||
|
||||
## Conversión de tipos de parámetros de query
|
||||
|
||||
También puedes declarar tipos `bool` y serán convertidos:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
En este caso, si vas a:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=1
|
||||
```
|
||||
|
||||
o
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=True
|
||||
```
|
||||
|
||||
o
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=true
|
||||
```
|
||||
|
||||
o
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=on
|
||||
```
|
||||
|
||||
o
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo?short=yes
|
||||
```
|
||||
|
||||
o cualquier otra variación (mayúsculas, primera letra en mayúscula, etc.) tu función verá el parámetro `short` con un valor `bool` de `True`. Si no, lo verá como `False`.
|
||||
|
||||
## Múltiples parámetros de path y query
|
||||
|
||||
Puedes declarar múltiples parámetros de path y parámetros de query al mismo tiempo. **FastAPI** sabe cuál es cuál.
|
||||
|
||||
No los tienes que declarar en un orden específico.
|
||||
|
||||
Serán detectados por nombre:
|
||||
|
||||
```Python hl_lines="8 10"
|
||||
{!../../../docs_src/query_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
## Parámetros de query requeridos
|
||||
|
||||
Cuando declaras un valor por defecto para los parámetros que no son de path (por ahora solo hemos visto parámetros de query), entonces no es requerido.
|
||||
|
||||
Si no quieres añadir un valor específico sino solo hacerlo opcional, pon el valor por defecto como `None`.
|
||||
|
||||
Pero cuando quieres hacer que un parámetro de query sea requerido, puedes simplemente no declararle un valor por defecto:
|
||||
|
||||
```Python hl_lines="6-7"
|
||||
{!../../../docs_src/query_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
Aquí el parámetro de query `needy` es un parámetro de query requerido, del tipo `str`.
|
||||
|
||||
Si abres tu navegador en una URL como:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo-item
|
||||
```
|
||||
|
||||
...sin añadir el parámetro `needy` requerido, verás un error como:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"query",
|
||||
"needy"
|
||||
],
|
||||
"msg": "field required",
|
||||
"type": "value_error.missing"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Dado que `needy` es un parámetro requerido necesitarías declararlo en la URL:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
||||
```
|
||||
|
||||
...esto funcionaría:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"item_id": "foo-item",
|
||||
"needy": "sooooneedy"
|
||||
}
|
||||
```
|
||||
|
||||
Por supuesto que también puedes definir algunos parámetros como requeridos, con un valor por defecto y otros completamente opcionales:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params/tutorial006.py!}
|
||||
```
|
||||
|
||||
En este caso hay 3 parámetros de query:
|
||||
|
||||
* `needy`, un `str` requerido.
|
||||
* `skip`, un `int` con un valor por defecto de `0`.
|
||||
* `limit`, un `int` opcional.
|
||||
|
||||
!!! tip "Consejo"
|
||||
También podrías usar los `Enum`s de la misma manera que con los [Parámetros de path](path-params.md#predefined-values){.internal-link target=_blank}.
|
||||
@@ -58,6 +58,7 @@ nav:
|
||||
- tutorial/index.md
|
||||
- tutorial/first-steps.md
|
||||
- tutorial/path-params.md
|
||||
- tutorial/query-params.md
|
||||
- Guía de Usuario Avanzada:
|
||||
- advanced/index.md
|
||||
- async.md
|
||||
|
||||
@@ -5,5 +5,5 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/typer")
|
||||
async def read_typer():
|
||||
async def redirect_typer():
|
||||
return RedirectResponse("https://typer.tiangolo.com")
|
||||
|
||||
9
docs_src/custom_response/tutorial006b.py
Normal file
9
docs_src/custom_response/tutorial006b.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/fastapi", response_class=RedirectResponse)
|
||||
async def redirect_fastapi():
|
||||
return "https://fastapi.tiangolo.com"
|
||||
9
docs_src/custom_response/tutorial006c.py
Normal file
9
docs_src/custom_response/tutorial006c.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
|
||||
async def redirect_pydantic():
|
||||
return "https://pydantic-docs.helpmanual.io/"
|
||||
10
docs_src/custom_response/tutorial009b.py
Normal file
10
docs_src/custom_response/tutorial009b.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
some_file_path = "large-video-file.mp4"
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/", response_class=FileResponse)
|
||||
async def main():
|
||||
return some_file_path
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.65.3"
|
||||
__version__ = "0.66.0"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ class FastAPI(Starlette):
|
||||
endpoint: Callable[..., Coroutine[Any, Any, Response]],
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -258,7 +258,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -351,7 +351,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -400,7 +400,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -449,7 +449,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -498,7 +498,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -547,7 +547,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -596,7 +596,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -645,7 +645,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -694,7 +694,7 @@ class FastAPI(Starlette):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
|
||||
@@ -36,9 +36,9 @@ def jsonable_encoder(
|
||||
custom_encoder: Dict[Any, Callable[[Any], Any]] = {},
|
||||
sqlalchemy_safe: bool = True,
|
||||
) -> Any:
|
||||
if include is not None and not isinstance(include, set):
|
||||
if include is not None and not isinstance(include, (set, dict)):
|
||||
include = set(include)
|
||||
if exclude is not None and not isinstance(exclude, set):
|
||||
if exclude is not None and not isinstance(exclude, (set, dict)):
|
||||
exclude = set(exclude)
|
||||
if isinstance(obj, BaseModel):
|
||||
encoder = getattr(obj.__config__, "json_encoders", {})
|
||||
|
||||
@@ -117,6 +117,9 @@ class SchemaBase(BaseModel):
|
||||
example: Optional[Any] = None
|
||||
deprecated: Optional[bool] = None
|
||||
|
||||
class Config:
|
||||
extra: str = "allow"
|
||||
|
||||
|
||||
class Schema(SchemaBase):
|
||||
allOf: Optional[List[SchemaBase]] = None
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import http.client
|
||||
import inspect
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
|
||||
|
||||
@@ -218,7 +219,19 @@ def get_openapi_path(
|
||||
)
|
||||
callbacks[callback.name] = {callback.path: cb_path}
|
||||
operation["callbacks"] = callbacks
|
||||
status_code = str(route.status_code)
|
||||
if route.status_code is not None:
|
||||
status_code = str(route.status_code)
|
||||
else:
|
||||
# It would probably make more sense for all response classes to have an
|
||||
# explicit default status_code, and to extract it from them, instead of
|
||||
# doing this inspection tricks, that would probably be in the future
|
||||
# TODO: probably make status_code a default class attribute for all
|
||||
# responses in Starlette
|
||||
response_signature = inspect.signature(current_response_class.__init__)
|
||||
status_code_param = response_signature.parameters.get("status_code")
|
||||
if status_code_param is not None:
|
||||
if isinstance(status_code_param.default, int):
|
||||
status_code = str(status_code_param.default)
|
||||
operation.setdefault("responses", {}).setdefault(status_code, {})[
|
||||
"description"
|
||||
] = route.response_description
|
||||
|
||||
@@ -154,7 +154,7 @@ async def run_endpoint_function(
|
||||
def get_request_handler(
|
||||
dependant: Dependant,
|
||||
body_field: Optional[ModelField] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
response_class: Union[Type[Response], DefaultPlaceholder] = Default(JSONResponse),
|
||||
response_field: Optional[ModelField] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
@@ -232,11 +232,12 @@ def get_request_handler(
|
||||
exclude_none=response_model_exclude_none,
|
||||
is_coroutine=is_coroutine,
|
||||
)
|
||||
response = actual_response_class(
|
||||
content=response_data,
|
||||
status_code=status_code,
|
||||
background=background_tasks, # type: ignore # in Starlette
|
||||
)
|
||||
response_args: Dict[str, Any] = {"background": background_tasks}
|
||||
# If status_code was set, use it, otherwise use the default from the
|
||||
# response class, in the case of redirect it's 307
|
||||
if status_code is not None:
|
||||
response_args["status_code"] = status_code
|
||||
response = actual_response_class(response_data, **response_args)
|
||||
response.headers.raw.extend(sub_response.headers.raw)
|
||||
if sub_response.status_code:
|
||||
response.status_code = sub_response.status_code
|
||||
@@ -293,7 +294,7 @@ class APIRoute(routing.Route):
|
||||
endpoint: Callable[..., Any],
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -469,7 +470,7 @@ class APIRouter(routing.Router):
|
||||
endpoint: Callable[..., Any],
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -541,7 +542,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -719,7 +720,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -769,7 +770,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -819,7 +820,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -869,7 +870,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -919,7 +920,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -969,7 +970,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -1019,7 +1020,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
@@ -1069,7 +1070,7 @@ class APIRouter(routing.Router):
|
||||
path: str,
|
||||
*,
|
||||
response_model: Optional[Type[Any]] = None,
|
||||
status_code: int = 200,
|
||||
status_code: Optional[int] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
dependencies: Optional[Sequence[params.Depends]] = None,
|
||||
summary: Optional[str] = None,
|
||||
|
||||
51
tests/test_custom_schema_fields.py
Normal file
51
tests/test_custom_schema_fields.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"x-something-internal": {"level": 4},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/foo", response_model=Item)
|
||||
def foo():
|
||||
return {"name": "Foo item"}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
item_schema = {
|
||||
"title": "Item",
|
||||
"required": ["name"],
|
||||
"type": "object",
|
||||
"x-something-internal": {
|
||||
"level": 4,
|
||||
},
|
||||
"properties": {
|
||||
"name": {
|
||||
"title": "Name",
|
||||
"type": "string",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_custom_response_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json()["components"]["schemas"]["Item"] == item_schema
|
||||
|
||||
|
||||
def test_response():
|
||||
# For coverage
|
||||
response = client.get("/foo")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Foo item"}
|
||||
174
tests/test_response_model_include_exclude.py
Normal file
174
tests/test_response_model_include_exclude.py
Normal file
@@ -0,0 +1,174 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Test(BaseModel):
|
||||
foo: str
|
||||
bar: str
|
||||
|
||||
|
||||
class Test2(BaseModel):
|
||||
test: Test
|
||||
baz: str
|
||||
|
||||
|
||||
class Test3(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
test2: Test2
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/simple_include",
|
||||
response_model=Test2,
|
||||
response_model_include={"baz": ..., "test": {"foo"}},
|
||||
)
|
||||
def simple_include():
|
||||
return Test2(
|
||||
test=Test(foo="simple_include test foo", bar="simple_include test bar"),
|
||||
baz="simple_include test2 baz",
|
||||
)
|
||||
|
||||
|
||||
@app.get(
|
||||
"/simple_include_dict",
|
||||
response_model=Test2,
|
||||
response_model_include={"baz": ..., "test": {"foo"}},
|
||||
)
|
||||
def simple_include_dict():
|
||||
return {
|
||||
"test": {
|
||||
"foo": "simple_include_dict test foo",
|
||||
"bar": "simple_include_dict test bar",
|
||||
},
|
||||
"baz": "simple_include_dict test2 baz",
|
||||
}
|
||||
|
||||
|
||||
@app.get(
|
||||
"/simple_exclude",
|
||||
response_model=Test2,
|
||||
response_model_exclude={"test": {"bar"}},
|
||||
)
|
||||
def simple_exclude():
|
||||
return Test2(
|
||||
test=Test(foo="simple_exclude test foo", bar="simple_exclude test bar"),
|
||||
baz="simple_exclude test2 baz",
|
||||
)
|
||||
|
||||
|
||||
@app.get(
|
||||
"/simple_exclude_dict",
|
||||
response_model=Test2,
|
||||
response_model_exclude={"test": {"bar"}},
|
||||
)
|
||||
def simple_exclude_dict():
|
||||
return {
|
||||
"test": {
|
||||
"foo": "simple_exclude_dict test foo",
|
||||
"bar": "simple_exclude_dict test bar",
|
||||
},
|
||||
"baz": "simple_exclude_dict test2 baz",
|
||||
}
|
||||
|
||||
|
||||
@app.get(
|
||||
"/mixed",
|
||||
response_model=Test3,
|
||||
response_model_include={"test2", "name"},
|
||||
response_model_exclude={"test2": {"baz"}},
|
||||
)
|
||||
def mixed():
|
||||
return Test3(
|
||||
name="mixed test3 name",
|
||||
age=3,
|
||||
test2=Test2(
|
||||
test=Test(foo="mixed test foo", bar="mixed test bar"), baz="mixed test2 baz"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@app.get(
|
||||
"/mixed_dict",
|
||||
response_model=Test3,
|
||||
response_model_include={"test2", "name"},
|
||||
response_model_exclude={"test2": {"baz"}},
|
||||
)
|
||||
def mixed_dict():
|
||||
return {
|
||||
"name": "mixed_dict test3 name",
|
||||
"age": 3,
|
||||
"test2": {
|
||||
"test": {"foo": "mixed_dict test foo", "bar": "mixed_dict test bar"},
|
||||
"baz": "mixed_dict test2 baz",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_nested_include_simple():
|
||||
response = client.get("/simple_include")
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
|
||||
assert response.json() == {
|
||||
"baz": "simple_include test2 baz",
|
||||
"test": {"foo": "simple_include test foo"},
|
||||
}
|
||||
|
||||
|
||||
def test_nested_include_simple_dict():
|
||||
response = client.get("/simple_include_dict")
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
|
||||
assert response.json() == {
|
||||
"baz": "simple_include_dict test2 baz",
|
||||
"test": {"foo": "simple_include_dict test foo"},
|
||||
}
|
||||
|
||||
|
||||
def test_nested_exclude_simple():
|
||||
response = client.get("/simple_exclude")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"baz": "simple_exclude test2 baz",
|
||||
"test": {"foo": "simple_exclude test foo"},
|
||||
}
|
||||
|
||||
|
||||
def test_nested_exclude_simple_dict():
|
||||
response = client.get("/simple_exclude_dict")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"baz": "simple_exclude_dict test2 baz",
|
||||
"test": {"foo": "simple_exclude_dict test foo"},
|
||||
}
|
||||
|
||||
|
||||
def test_nested_include_mixed():
|
||||
response = client.get("/mixed")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"name": "mixed test3 name",
|
||||
"test2": {
|
||||
"test": {"foo": "mixed test foo", "bar": "mixed test bar"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_nested_include_mixed_dict():
|
||||
response = client.get("/mixed_dict")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"name": "mixed_dict test3 name",
|
||||
"test2": {
|
||||
"test": {"foo": "mixed_dict test foo", "bar": "mixed_dict test bar"},
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,32 @@ from docs_src.custom_response.tutorial006 import app
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/typer": {
|
||||
"get": {
|
||||
"summary": "Redirect Typer",
|
||||
"operationId": "redirect_typer_typer_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_get():
|
||||
response = client.get("/typer", allow_redirects=False)
|
||||
assert response.status_code == 307, response.text
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.custom_response.tutorial006b import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/fastapi": {
|
||||
"get": {
|
||||
"summary": "Redirect Fastapi",
|
||||
"operationId": "redirect_fastapi_fastapi_get",
|
||||
"responses": {"307": {"description": "Successful Response"}},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_redirect_response_class():
|
||||
response = client.get("/fastapi", allow_redirects=False)
|
||||
assert response.status_code == 307
|
||||
assert response.headers["location"] == "https://fastapi.tiangolo.com"
|
||||
@@ -0,0 +1,32 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.custom_response.tutorial006c import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/pydantic": {
|
||||
"get": {
|
||||
"summary": "Redirect Pydantic",
|
||||
"operationId": "redirect_pydantic_pydantic_get",
|
||||
"responses": {"302": {"description": "Successful Response"}},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_redirect_status_code():
|
||||
response = client.get("/pydantic", allow_redirects=False)
|
||||
assert response.status_code == 302
|
||||
assert response.headers["location"] == "https://pydantic-docs.helpmanual.io/"
|
||||
17
tests/test_tutorial/test_custom_response/test_tutorial009.py
Normal file
17
tests/test_tutorial/test_custom_response/test_tutorial009.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.custom_response import tutorial009
|
||||
from docs_src.custom_response.tutorial009 import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_get(tmp_path: Path):
|
||||
file_path: Path = tmp_path / "large-video-file.mp4"
|
||||
tutorial009.some_file_path = str(file_path)
|
||||
test_content = b"Fake video bytes"
|
||||
file_path.write_bytes(test_content)
|
||||
response = client.get("/")
|
||||
assert response.content == test_content
|
||||
@@ -0,0 +1,17 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from docs_src.custom_response import tutorial009b
|
||||
from docs_src.custom_response.tutorial009b import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_get(tmp_path: Path):
|
||||
file_path: Path = tmp_path / "large-video-file.mp4"
|
||||
tutorial009b.some_file_path = str(file_path)
|
||||
test_content = b"Fake video bytes"
|
||||
file_path.write_bytes(test_content)
|
||||
response = client.get("/")
|
||||
assert response.content == test_content
|
||||
Reference in New Issue
Block a user