Browse Source

Merge branch 'tiangolo:master' into dev-getquery

pull/4573/head
gyudoza 2 years ago
committed by GitHub
parent
commit
8cf7ce9d9d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/en/data/external_links.yml
  2. 16
      docs/en/docs/release-notes.md
  3. 70
      docs/en/docs/tutorial/response-model.md
  4. 336
      docs/tr/docs/tutorial/first_steps.md
  5. 2
      docs/tr/mkdocs.yml
  6. 11
      docs_src/response_model/tutorial003_02.py
  7. 9
      docs_src/response_model/tutorial003_03.py
  8. 13
      docs_src/response_model/tutorial003_04.py
  9. 11
      docs_src/response_model/tutorial003_04_py310.py
  10. 13
      docs_src/response_model/tutorial003_05.py
  11. 11
      docs_src/response_model/tutorial003_05_py310.py
  12. 2
      fastapi/__init__.py
  13. 7
      fastapi/routing.py
  14. 8
      fastapi/utils.py
  15. 4
      pyproject.toml
  16. 60
      tests/test_response_model_as_return_annotation.py
  17. 120
      tests/test_tutorial/test_response_model/test_tutorial003_01.py
  18. 129
      tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py
  19. 93
      tests/test_tutorial/test_response_model/test_tutorial003_02.py
  20. 36
      tests/test_tutorial/test_response_model/test_tutorial003_03.py
  21. 9
      tests/test_tutorial/test_response_model/test_tutorial003_04.py
  22. 12
      tests/test_tutorial/test_response_model/test_tutorial003_04_py310.py
  23. 93
      tests/test_tutorial/test_response_model/test_tutorial003_05.py
  24. 103
      tests/test_tutorial/test_response_model/test_tutorial003_05_py310.py

4
docs/en/data/external_links.yml

@ -1,5 +1,9 @@
articles:
english:
- author: Raf Rasenberg
author_link: https://rafrasenberg.com/about/
link: https://rafrasenberg.com/fastapi-lambda/
title: 'FastAPI lambda container: serverless simplified'
- author: Teresa N. Fontanella De Santis
author_link: https://dev.to/
link: https://dev.to/teresafds/authorization-on-fastapi-with-casbin-41og

16
docs/en/docs/release-notes.md

@ -2,6 +2,22 @@
## Latest Changes
## 0.89.1
### Fixes
* 🐛 Ignore Response classes on return annotation. PR [#5855](https://github.com/tiangolo/fastapi/pull/5855) by [@Kludex](https://github.com/Kludex). See the new docs in the PR below.
### Docs
* 📝 Update docs and examples for Response Model with Return Type Annotations, and update runtime error. PR [#5873](https://github.com/tiangolo/fastapi/pull/5873) by [@tiangolo](https://github.com/tiangolo). New docs at [Response Model - Return Type: Other Return Type Annotations](https://fastapi.tiangolo.com/tutorial/response-model/#other-return-type-annotations).
* 📝 Add External Link: FastAPI lambda container: serverless simplified. PR [#5784](https://github.com/tiangolo/fastapi/pull/5784) by [@rafrasenberg](https://github.com/rafrasenberg).
### Translations
* 🌐 Add Turkish translation for `docs/tr/docs/tutorial/first_steps.md`. PR [#5691](https://github.com/tiangolo/fastapi/pull/5691) by [@Kadermiyanyedi](https://github.com/Kadermiyanyedi).
## 0.89.0
### Features

70
docs/en/docs/tutorial/response-model.md

@ -89,6 +89,8 @@ If you declare both a return type and a `response_model`, the `response_model` w
This way you can add correct type annotations to your functions even when you are returning a type different than the response model, to be used by the editor and tools like mypy. And still you can have FastAPI do the data validation, documentation, etc. using the `response_model`.
You can also use `response_model=None` to disable creating a response model for that *path operation*, you might need to do it if you are adding type annotations for things that are not valid Pydantic fields, you will see an example of that in one of the sections below.
## Return the same input data
Here we are declaring a `UserIn` model, it will contain a plaintext password:
@ -244,6 +246,74 @@ And both models will be used for the interactive API documentation:
<img src="/img/tutorial/response-model/image02.png">
## Other Return Type Annotations
There might be cases where you return something that is not a valid Pydantic field and you annotate it in the function, only to get the support provided by tooling (the editor, mypy, etc).
### Return a Response Directly
The most common case would be [returning a Response directly as explained later in the advanced docs](../advanced/response-directly.md){.internal-link target=_blank}.
```Python hl_lines="8 10-11"
{!> ../../../docs_src/response_model/tutorial003_02.py!}
```
This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass) of `Response`.
And tools will also be happy because both `RedirectResponse` and `JSONResponse` are subclasses of `Response`, so the type annotation is correct.
### Annotate a Response Subclass
You can also use a subclass of `Response` in the type annotation:
```Python hl_lines="8-9"
{!> ../../../docs_src/response_model/tutorial003_03.py!}
```
This will also work because `RedirectResponse` is a subclass of `Response`, and FastAPI will automatically handle this simple case.
### Invalid Return Type Annotations
But when you return some other arbitrary object that is not a valid Pydantic type (e.g. a database object) and you annotate it like that in the function, FastAPI will try to create a Pydantic response model from that type annotation, and will fail.
The same would happen if you had something like a <abbr title='A union between multiple types means "any of these types".'>union</abbr> between different types where one or more of them are not valid Pydantic types, for example this would fail 💥:
=== "Python 3.6 and above"
```Python hl_lines="10"
{!> ../../../docs_src/response_model/tutorial003_04.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="8"
{!> ../../../docs_src/response_model/tutorial003_04_py310.py!}
```
...this fails because the type annotation is not a Pydantic type and is not just a single `Response` class or subclass, it's a union (any of the two) between a `Response` and a `dict`.
### Disable Response Model
Continuing from the example above, you might not want to have the default data validation, documentation, filtering, etc. that is performed by FastAPI.
But you might want to still keep the return type annotation in the function to get the support from tools like editors and type checkers (e.g. mypy).
In this case, you can disable the response model generation by setting `response_model=None`:
=== "Python 3.6 and above"
```Python hl_lines="9"
{!> ../../../docs_src/response_model/tutorial003_05.py!}
```
=== "Python 3.10 and above"
```Python hl_lines="7"
{!> ../../../docs_src/response_model/tutorial003_05_py310.py!}
```
This will make FastAPI skip the response model generation and that way you can have any return type annotations you need without it affecting your FastAPI application. 🤓
## Response Model encoding parameters
Your response model could have default values, like:

336
docs/tr/docs/tutorial/first_steps.md

@ -0,0 +1,336 @@
# İlk Adımlar
En basit FastAPI dosyası şu şekildedir:
```Python
{!../../../docs_src/first_steps/tutorial001.py!}
```
Bunu bir `main.py` dosyasına kopyalayın.
Projeyi çalıştırın:
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
<span style="color: green;">INFO</span>: Started reloader process [28720]
<span style="color: green;">INFO</span>: Started server process [28722]
<span style="color: green;">INFO</span>: Waiting for application startup.
<span style="color: green;">INFO</span>: Application startup complete.
```
</div>
!!! note
`uvicorn main:app` komutu şunu ifade eder:
* `main`: `main.py` dosyası (the Python "module").
* `app`: `main.py` dosyası içerisinde `app = FastAPI()` satırıyla oluşturulan nesne.
* `--reload`: Kod değişikliği sonrasında sunucunun yeniden başlatılmasını sağlar. Yalnızca geliştirme için kullanın.
Çıktıda şu şekilde bir satır vardır:
```hl_lines="4"
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
Bu satır, yerel makinenizde uygulamanızın sunulduğu URL'yi gösterir.
### Kontrol Et
Tarayıcınızda <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a> adresini açın.
Bir JSON yanıtı göreceksiniz:
```JSON
{"message": "Hello World"}
```
### İnteraktif API dokümantasyonu
<a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> adresine gidin.
Otomatik oluşturulmuş( <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a> tarafından sağlanan) interaktif bir API dokümanı göreceksiniz:
![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png)
### Alternatif API dokümantasyonu
Şimdi, <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a> adresine gidin.
Otomatik oluşturulmuş(<a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a> tarafından sağlanan) bir API dokümanı göreceksiniz:
![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png)
### OpenAPI
**FastAPI**, **OpenAPI** standardını kullanarak tüm API'lerinizi açıklayan bir "şema" oluşturur.
#### "Şema"
Bir "şema", bir şeyin tanımı veya açıklamasıdır. Soyut bir açıklamadır, uygulayan kod değildir.
#### API "şemaları"
Bu durumda, <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank">OpenAPI</a>, API şemasını nasıl tanımlayacağınızı belirten şartnamelerdir.
Bu şema tanımı, API yollarınızı, aldıkları olası parametreleri vb. içerir.
#### Data "şema"
"Şema" terimi, JSON içeriği gibi bazı verilerin şeklini de ifade edebilir.
Bu durumda, JSON öznitelikleri ve sahip oldukları veri türleri vb. anlamına gelir.
#### OpenAPI and JSON Şema
OpenAPI, API'niz için bir API şeması tanımlar. Ve bu şema, JSON veri şemaları standardı olan **JSON Şema** kullanılarak API'niz tarafından gönderilen ve alınan verilerin tanımlarını (veya "şemalarını") içerir.
#### `openapi.json` kontrol et
OpenAPI şemasının nasıl göründüğünü merak ediyorsanız, FastAPI otomatik olarak tüm API'nizin açıklamalarını içeren bir JSON (şema) oluşturur.
Doğrudan şu adreste görebilirsiniz: <a href="http://127.0.0.1:8000/openapi.json" class="external-link" target="_blank">http://127.0.0.1:8000/openapi.json</a>.
Aşağıdaki gibi bir şeyle başlayan bir JSON gösterecektir:
```JSON
{
"openapi": "3.0.2",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
...
```
#### OpenAPI ne içindir?
OpenAPI şeması, dahili olarak bulunan iki etkileşimli dokümantasyon sistemine güç veren şeydir.
Ve tamamen OpenAPI'ye dayalı düzinelerce alternatif vardır. **FastAPI** ile oluşturulmuş uygulamanıza bu alternatiflerden herhangi birini kolayca ekleyebilirsiniz.
API'nizle iletişim kuran istemciler için otomatik olarak kod oluşturmak için de kullanabilirsiniz. Örneğin, frontend, mobil veya IoT uygulamaları.
## Adım adım özet
### Adım 1: `FastAPI`yi içe aktarın
```Python hl_lines="1"
{!../../../docs_src/first_steps/tutorial001.py!}
```
`FastAPI`, API'niz için tüm fonksiyonları sağlayan bir Python sınıfıdır.
!!! note "Teknik Detaylar"
`FastAPI` doğrudan `Starlette` kalıtım alan bir sınıftır.
Tüm <a href="https://www.starlette.io/" class="external-link" target="_blank">Starlette</a> fonksiyonlarını `FastAPI` ile de kullanabilirsiniz.
### Adım 2: Bir `FastAPI` örneği oluşturun
```Python hl_lines="3"
{!../../../docs_src/first_steps/tutorial001.py!}
```
Burada `app` değişkeni `FastAPI` sınıfının bir örneği olacaktır.
Bu tüm API'yi oluşturmak için ana etkileşim noktası olacaktır.
`uvicorn` komutunda atıfta bulunulan `app` ile aynıdır.
<div class="termy">
```console
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Uygulamanızı aşağıdaki gibi oluşturursanız:
```Python hl_lines="3"
{!../../../docs_src/first_steps/tutorial002.py!}
```
Ve bunu `main.py` dosyasına koyduktan sonra `uvicorn` komutunu şu şekilde çağırabilirsiniz:
<div class="termy">
```console
$ uvicorn main:my_awesome_api --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Adım 3: *Path işlemleri* oluşturmak
#### Path
Burada "Path" URL'de ilk "\" ile başlayan son bölümü ifade eder.
Yani, şu şekilde bir URL'de:
```
https://example.com/items/foo
```
... path şöyle olabilir:
```
/items/foo
```
!!! info
Genellikle bir "path", "endpoint" veya "route" olarak adlandırılabilir.
Bir API oluştururken, "path", "resource" ile "concern" ayırmanın ana yoludur.
#### İşlemler
Burada "işlem" HTTP methodlarından birini ifade eder.
Onlardan biri:
* `POST`
* `GET`
* `PUT`
* `DELETE`
... ve daha egzotik olanları:
* `OPTIONS`
* `HEAD`
* `PATCH`
* `TRACE`
HTTP protokolünde, bu "methodlardan" birini (veya daha fazlasını) kullanarak her path ile iletişim kurabilirsiniz.
---
API'lerinizi oluştururkan, belirli bir işlemi gerçekleştirirken belirli HTTP methodlarını kullanırsınız.
Normalde kullanılan:
* `POST`: veri oluşturmak.
* `GET`: veri okumak.
* `PUT`: veriyi güncellemek.
* `DELETE`: veriyi silmek.
Bu nedenle, OpenAPI'de HTTP methodlarından her birine "işlem" denir.
Bizde onlara "**işlemler**" diyeceğiz.
#### Bir *Path işlem decoratorleri* tanımlanmak
```Python hl_lines="6"
{!../../../docs_src/first_steps/tutorial001.py!}
```
`@app.get("/")` **FastAPI'ye** aşağıdaki fonksiyonun adresine giden istekleri işlemekten sorumlu olduğunu söyler:
* path `/`
* <abbr title="an HTTP GET method"><code>get</code> işlemi</abbr> kullanılarak
!!! info "`@decorator` Bilgisi"
Python `@something` şeklinde ifadeleri "decorator" olarak adlandırır.
Decoratoru bir fonksiyonun üzerine koyarsınız. Dekoratif bir şapka gibi (Sanırım terim buradan gelmektedir).
Bir "decorator" fonksiyonu alır ve bazı işlemler gerçekleştir.
Bizim durumumzda decarator **FastAPI'ye** fonksiyonun bir `get` işlemi ile `/` pathine geldiğini söyler.
Bu **path işlem decoratordür**
Ayrıca diğer işlemleri de kullanabilirsiniz:
* `@app.post()`
* `@app.put()`
* `@app.delete()`
Ve daha egzotik olanları:
* `@app.options()`
* `@app.head()`
* `@app.patch()`
* `@app.trace()`
!!! tip
Her işlemi (HTTP method) istediğiniz gibi kullanmakta özgürsünüz.
**FastAPI** herhangi bir özel anlamı zorlamaz.
Buradaki bilgiler bir gereklilik değil, bir kılavuz olarak sunulmaktadır.
Örneğin, GraphQL kullanırkan normalde tüm işlemleri yalnızca `POST` işlemini kullanarak gerçekleştirirsiniz.
### Adım 4: **path işlem fonksiyonunu** tanımlayın
Aşağıdakiler bizim **path işlem fonksiyonlarımızdır**:
* **path**: `/`
* **işlem**: `get`
* **function**: "decorator"ün altındaki fonksiyondur (`@app.get("/")` altında).
```Python hl_lines="7"
{!../../../docs_src/first_steps/tutorial001.py!}
```
Bu bir Python fonksiyonudur.
Bir `GET` işlemi kullanarak "`/`" URL'sine bir istek geldiğinde **FastAPI** tarafından çağrılır.
Bu durumda bir `async` fonksiyonudur.
---
Bunu `async def` yerine normal bir fonksiyon olarakta tanımlayabilirsiniz.
```Python hl_lines="7"
{!../../../docs_src/first_steps/tutorial003.py!}
```
!!! note
Eğer farkı bilmiyorsanız, [Async: *"Acelesi var?"*](../async.md#in-a-hurry){.internal-link target=_blank} kontrol edebilirsiniz.
### Adım 5: İçeriği geri döndürün
```Python hl_lines="8"
{!../../../docs_src/first_steps/tutorial001.py!}
```
Bir `dict`, `list` döndürebilir veya `str`, `int` gibi tekil değerler döndürebilirsiniz.
Ayrıca, Pydantic modellerini de döndürebilirsiniz. (Bununla ilgili daha sonra ayrıntılı bilgi göreceksiniz.)
Otomatik olarak JSON'a dönüştürülecek(ORM'ler vb. dahil) başka birçok nesne ve model vardır. En beğendiklerinizi kullanmayı deneyin, yüksek ihtimalle destekleniyordur.
## Özet
* `FastAPI`'yi içe aktarın.
* Bir `app` örneği oluşturun.
* **path işlem decorator** yazın. (`@app.get("/")` gibi)
* **path işlem fonksiyonu** yazın. (`def root(): ...` gibi)
* Development sunucunuzu çalıştırın. (`uvicorn main:app --reload` gibi)

2
docs/tr/mkdocs.yml

@ -61,6 +61,8 @@ nav:
- features.md
- fastapi-people.md
- python-types.md
- Tutorial - User Guide:
- tutorial/first-steps.md
markdown_extensions:
- toc:
permalink: true

11
docs_src/response_model/tutorial003_02.py

@ -0,0 +1,11 @@
from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse, RedirectResponse
app = FastAPI()
@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return JSONResponse(content={"message": "Here's your interdimensional portal."})

9
docs_src/response_model/tutorial003_03.py

@ -0,0 +1,9 @@
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/teleport")
async def get_teleport() -> RedirectResponse:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")

13
docs_src/response_model/tutorial003_04.py

@ -0,0 +1,13 @@
from typing import Union
from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/portal")
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}

11
docs_src/response_model/tutorial003_04_py310.py

@ -0,0 +1,11 @@
from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response | dict:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}

13
docs_src/response_model/tutorial003_05.py

@ -0,0 +1,13 @@
from typing import Union
from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}

11
docs_src/response_model/tutorial003_05_py310.py

@ -0,0 +1,11 @@
from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Response | dict:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}

2
fastapi/__init__.py

@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.89.0"
__version__ = "0.89.1"
from starlette import status as status

7
fastapi/routing.py

@ -42,6 +42,7 @@ from fastapi.utils import (
from pydantic import BaseModel
from pydantic.error_wrappers import ErrorWrapper, ValidationError
from pydantic.fields import ModelField, Undefined
from pydantic.utils import lenient_issubclass
from starlette import routing
from starlette.concurrency import run_in_threadpool
from starlette.exceptions import HTTPException
@ -356,7 +357,11 @@ class APIRoute(routing.Route):
self.path = path
self.endpoint = endpoint
if isinstance(response_model, DefaultPlaceholder):
response_model = get_typed_return_annotation(endpoint)
return_annotation = get_typed_return_annotation(endpoint)
if lenient_issubclass(return_annotation, Response):
response_model = None
else:
response_model = return_annotation
self.response_model = response_model
self.summary = summary
self.response_description = response_description

8
fastapi/utils.py

@ -88,7 +88,13 @@ def create_response_field(
return response_field(field_info=field_info)
except RuntimeError:
raise fastapi.exceptions.FastAPIError(
f"Invalid args for response field! Hint: check that {type_} is a valid pydantic field type"
"Invalid args for response field! Hint: "
f"check that {type_} is a valid Pydantic field type. "
"If you are using a return type annotation that is not a valid Pydantic "
"field (e.g. Union[Response, dict, None]) you can disable generating the "
"response model from the type annotation with the path operation decorator "
"parameter response_model=None. Read more: "
"https://fastapi.tiangolo.com/tutorial/response-model/"
) from None

4
pyproject.toml

@ -155,6 +155,10 @@ source = [
"fastapi"
]
context = '${CONTEXT}'
omit = [
"docs_src/response_model/tutorial003_04.py",
"docs_src/response_model/tutorial003_04_py310.py",
]
[tool.ruff]
select = [

60
tests/test_response_model_as_return_annotation.py

@ -2,6 +2,8 @@ from typing import List, Union
import pytest
from fastapi import FastAPI
from fastapi.exceptions import FastAPIError
from fastapi.responses import JSONResponse, Response
from fastapi.testclient import TestClient
from pydantic import BaseModel, ValidationError
@ -237,6 +239,16 @@ def no_response_model_annotation_union_return_model2() -> Union[User, Item]:
return Item(name="Foo", price=42.0)
@app.get("/no_response_model-annotation_response_class")
def no_response_model_annotation_response_class() -> Response:
return Response(content="Foo")
@app.get("/no_response_model-annotation_json_response_class")
def no_response_model_annotation_json_response_class() -> JSONResponse:
return JSONResponse(content={"foo": "bar"})
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
@ -789,6 +801,30 @@ openapi_schema = {
},
}
},
"/no_response_model-annotation_response_class": {
"get": {
"summary": "No Response Model Annotation Response Class",
"operationId": "no_response_model_annotation_response_class_no_response_model_annotation_response_class_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
},
"/no_response_model-annotation_json_response_class": {
"get": {
"summary": "No Response Model Annotation Json Response Class",
"operationId": "no_response_model_annotation_json_response_class_no_response_model_annotation_json_response_class_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
},
},
"components": {
"schemas": {
@ -1049,3 +1085,27 @@ def test_no_response_model_annotation_union_return_model2():
response = client.get("/no_response_model-annotation_union-return_model2")
assert response.status_code == 200, response.text
assert response.json() == {"name": "Foo", "price": 42.0}
def test_no_response_model_annotation_return_class():
response = client.get("/no_response_model-annotation_response_class")
assert response.status_code == 200, response.text
assert response.text == "Foo"
def test_no_response_model_annotation_json_response_class():
response = client.get("/no_response_model-annotation_json_response_class")
assert response.status_code == 200, response.text
assert response.json() == {"foo": "bar"}
def test_invalid_response_model_field():
app = FastAPI()
with pytest.raises(FastAPIError) as e:
@app.get("/")
def read_root() -> Union[Response, None]:
return Response(content="Foo") # pragma: no cover
assert "valid Pydantic field type" in e.value.args[0]
assert "parameter response_model=None" in e.value.args[0]

120
tests/test_tutorial/test_response_model/test_tutorial003_01.py

@ -0,0 +1,120 @@
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial003_01 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/user/": {
"post": {
"summary": "Create User",
"operationId": "create_user_user__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/UserIn"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/BaseUser"}
}
},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"BaseUser": {
"title": "BaseUser",
"required": ["username", "email"],
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
"email": {"title": "Email", "type": "string", "format": "email"},
"full_name": {"title": "Full Name", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"UserIn": {
"title": "UserIn",
"required": ["username", "email", "password"],
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
"email": {"title": "Email", "type": "string", "format": "email"},
"full_name": {"title": "Full Name", "type": "string"},
"password": {"title": "Password", "type": "string"},
},
},
"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"},
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_post_user():
response = client.post(
"/user/",
json={
"username": "foo",
"password": "fighter",
"email": "foo@example.com",
"full_name": "Grave Dohl",
},
)
assert response.status_code == 200, response.text
assert response.json() == {
"username": "foo",
"email": "foo@example.com",
"full_name": "Grave Dohl",
}

129
tests/test_tutorial/test_response_model/test_tutorial003_01_py310.py

@ -0,0 +1,129 @@
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/user/": {
"post": {
"summary": "Create User",
"operationId": "create_user_user__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/UserIn"}
}
},
"required": True,
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/BaseUser"}
}
},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
}
}
},
"components": {
"schemas": {
"BaseUser": {
"title": "BaseUser",
"required": ["username", "email"],
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
"email": {"title": "Email", "type": "string", "format": "email"},
"full_name": {"title": "Full Name", "type": "string"},
},
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {"$ref": "#/components/schemas/ValidationError"},
}
},
},
"UserIn": {
"title": "UserIn",
"required": ["username", "email", "password"],
"type": "object",
"properties": {
"username": {"title": "Username", "type": "string"},
"email": {"title": "Email", "type": "string", "format": "email"},
"full_name": {"title": "Full Name", "type": "string"},
"password": {"title": "Password", "type": "string"},
},
},
"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"},
},
},
}
},
}
@pytest.fixture(name="client")
def get_client():
from docs_src.response_model.tutorial003_01_py310 import app
client = TestClient(app)
return client
@needs_py310
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
@needs_py310
def test_post_user(client: TestClient):
response = client.post(
"/user/",
json={
"username": "foo",
"password": "fighter",
"email": "foo@example.com",
"full_name": "Grave Dohl",
},
)
assert response.status_code == 200, response.text
assert response.json() == {
"username": "foo",
"email": "foo@example.com",
"full_name": "Grave Dohl",
}

93
tests/test_tutorial/test_response_model/test_tutorial003_02.py

@ -0,0 +1,93 @@
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial003_02 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/portal": {
"get": {
"summary": "Get Portal",
"operationId": "get_portal_portal_get",
"parameters": [
{
"required": False,
"schema": {
"title": "Teleport",
"type": "boolean",
"default": False,
},
"name": "teleport",
"in": "query",
}
],
"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"},
}
},
},
"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"},
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_portal():
response = client.get("/portal")
assert response.status_code == 200, response.text
assert response.json() == {"message": "Here's your interdimensional portal."}
def test_get_redirect():
response = client.get("/portal", params={"teleport": True}, follow_redirects=False)
assert response.status_code == 307, response.text
assert response.headers["location"] == "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

36
tests/test_tutorial/test_response_model/test_tutorial003_03.py

@ -0,0 +1,36 @@
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial003_03 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/teleport": {
"get": {
"summary": "Get Teleport",
"operationId": "get_teleport_teleport_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_portal():
response = client.get("/teleport", follow_redirects=False)
assert response.status_code == 307, response.text
assert response.headers["location"] == "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

9
tests/test_tutorial/test_response_model/test_tutorial003_04.py

@ -0,0 +1,9 @@
import pytest
from fastapi.exceptions import FastAPIError
def test_invalid_response_model():
with pytest.raises(FastAPIError):
from docs_src.response_model.tutorial003_04 import app
assert app # pragma: no cover

12
tests/test_tutorial/test_response_model/test_tutorial003_04_py310.py

@ -0,0 +1,12 @@
import pytest
from fastapi.exceptions import FastAPIError
from ...utils import needs_py310
@needs_py310
def test_invalid_response_model():
with pytest.raises(FastAPIError):
from docs_src.response_model.tutorial003_04_py310 import app
assert app # pragma: no cover

93
tests/test_tutorial/test_response_model/test_tutorial003_05.py

@ -0,0 +1,93 @@
from fastapi.testclient import TestClient
from docs_src.response_model.tutorial003_05 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/portal": {
"get": {
"summary": "Get Portal",
"operationId": "get_portal_portal_get",
"parameters": [
{
"required": False,
"schema": {
"title": "Teleport",
"type": "boolean",
"default": False,
},
"name": "teleport",
"in": "query",
}
],
"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"},
}
},
},
"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"},
},
},
}
},
}
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
def test_get_portal():
response = client.get("/portal")
assert response.status_code == 200, response.text
assert response.json() == {"message": "Here's your interdimensional portal."}
def test_get_redirect():
response = client.get("/portal", params={"teleport": True}, follow_redirects=False)
assert response.status_code == 307, response.text
assert response.headers["location"] == "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

103
tests/test_tutorial/test_response_model/test_tutorial003_05_py310.py

@ -0,0 +1,103 @@
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/portal": {
"get": {
"summary": "Get Portal",
"operationId": "get_portal_portal_get",
"parameters": [
{
"required": False,
"schema": {
"title": "Teleport",
"type": "boolean",
"default": False,
},
"name": "teleport",
"in": "query",
}
],
"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"},
}
},
},
"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"},
},
},
}
},
}
@pytest.fixture(name="client")
def get_client():
from docs_src.response_model.tutorial003_05_py310 import app
client = TestClient(app)
return client
@needs_py310
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == openapi_schema
@needs_py310
def test_get_portal(client: TestClient):
response = client.get("/portal")
assert response.status_code == 200, response.text
assert response.json() == {"message": "Here's your interdimensional portal."}
@needs_py310
def test_get_redirect(client: TestClient):
response = client.get("/portal", params={"teleport": True}, follow_redirects=False)
assert response.status_code == 307, response.text
assert response.headers["location"] == "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
Loading…
Cancel
Save