Requestbody¶
Wenn Sie Daten von einem Client (sagen wir, einem Browser) zu Ihrer API senden, dann senden Sie diese als einen Requestbody (Deutsch: Anfragekörper).
Ein Requestbody sind Daten, die vom Client zu Ihrer API gesendet werden. Ein Responsebody (Deutsch: Antwortkörper) sind Daten, die Ihre API zum Client sendet.
Ihre API sendet fast immer einen Responsebody. Aber Clients senden nicht unbedingt immer Requestbodys (sondern nur Metadaten).
Um einen Requestbody zu deklarieren, verwenden Sie Pydantic-Modelle mit allen deren Fähigkeiten und Vorzügen.
Info
Um Daten zu versenden, sollten Sie eines von: POST
(meistverwendet), PUT
, DELETE
oder PATCH
verwenden.
Senden Sie einen Body mit einem GET
-Request, dann führt das laut Spezifikation zu undefiniertem Verhalten. Trotzdem wird es von FastAPI unterstützt, für sehr komplexe/extreme Anwendungsfälle.
Da aber davon abgeraten wird, zeigt die interaktive Dokumentation mit Swagger-Benutzeroberfläche die Dokumentation für den Body auch nicht an, wenn GET
verwendet wird. Dazwischengeschaltete Proxys unterstützen es möglicherweise auch nicht.
Importieren Sie Pydantics BaseModel
¶
Zuerst müssen Sie BaseModel
von pydantic
importieren:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
Erstellen Sie Ihr Datenmodell¶
Dann deklarieren Sie Ihr Datenmodell als eine Klasse, die von BaseModel
erbt.
Verwenden Sie Standard-Python-Typen für die Klassenattribute:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
Wie auch bei Query-Parametern gilt, wenn ein Modellattribut einen Defaultwert hat, ist das Attribut nicht erforderlich. Ansonsten ist es erforderlich. Verwenden Sie None
, um es als optional zu kennzeichnen.
Zum Beispiel deklariert das obige Modell ein JSON "object
" (oder Python-dict
) wie dieses:
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
Da description
und tax
optional sind (mit None
als Defaultwert), wäre folgendes JSON "object
" auch gültig:
{
"name": "Foo",
"price": 45.2
}
Deklarieren Sie es als Parameter¶
Um es zu Ihrer Pfadoperation hinzuzufügen, deklarieren Sie es auf die gleiche Weise, wie Sie Pfad- und Query-Parameter deklariert haben:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
... und deklarieren Sie seinen Typ als das Modell, welches Sie erstellt haben, Item
.
Resultate¶
Mit nur dieser Python-Typdeklaration, wird FastAPI:
- Den Requestbody als JSON lesen.
- Die entsprechenden Typen konvertieren (falls nötig).
- Diese Daten validieren.
- Wenn die Daten ungültig sind, einen klar lesbaren Fehler zurückgeben, der anzeigt, wo und was die inkorrekten Daten waren.
- Ihnen die erhaltenen Daten im Parameter
item
übergeben.- Da Sie diesen in der Funktion als vom Typ
Item
deklariert haben, erhalten Sie die ganze Editor-Unterstützung (Autovervollständigung, usw.) für alle Attribute und deren Typen.
- Da Sie diesen in der Funktion als vom Typ
- Eine JSON Schema Definition für Ihr Modell generieren, welche Sie überall sonst verwenden können, wenn es für Ihr Projekt Sinn macht.
- Diese Schemas werden Teil des generierten OpenAPI-Schemas und werden von den UIs der automatischen Dokumentation verwendet.
Automatische Dokumentation¶
Die JSON-Schemas Ihrer Modelle werden Teil ihrer OpenAPI-generierten Schemas und werden in der interaktiven API Dokumentation angezeigt:
Und werden auch verwendet in der API-Dokumentation innerhalb jeder Pfadoperation, welche sie braucht:
Editor Unterstützung¶
In Ihrem Editor, innerhalb Ihrer Funktion, erhalten Sie Typhinweise und Code-Vervollständigung überall (was nicht der Fall wäre, wenn Sie ein dict
anstelle eines Pydantic Modells erhalten hätten):
Sie bekommen auch Fehler-Meldungen für inkorrekte Typoperationen:
Das ist nicht zufällig so, das ganze Framework wurde um dieses Design herum aufgebaut.
Und es wurde in der Designphase gründlich getestet, vor der Implementierung, um sicherzustellen, dass es mit jedem Editor funktioniert.
Es gab sogar ein paar Änderungen an Pydantic selbst, um das zu unterstützen.
Die vorherigen Screenshots zeigten Visual Studio Code.
Aber Sie bekommen die gleiche Editor-Unterstützung in PyCharm und in den meisten anderen Python-Editoren:
Tipp
Wenn Sie PyCharm als Ihren Editor verwenden, probieren Sie das Pydantic PyCharm Plugin aus.
Es verbessert die Editor-Unterstützung für Pydantic-Modelle, mit:
- Code-Vervollständigung
- Typüberprüfungen
- Refaktorisierung
- Suchen
- Inspektionen
Das Modell verwenden¶
Innerhalb der Funktion können Sie alle Attribute des Modells direkt verwenden:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
Requestbody- + Pfad-Parameter¶
Sie können Pfad- und Requestbody-Parameter gleichzeitig deklarieren.
FastAPI erkennt, dass Funktionsparameter, die mit Pfad-Parametern übereinstimmen, vom Pfad genommen werden sollen, und dass Funktionsparameter, welche Pydantic-Modelle sind, vom Requestbody genommen werden sollen.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}
Requestbody- + Pfad- + Query-Parameter¶
Sie können auch zur gleichen Zeit Body-, Pfad- und Query-Parameter deklarieren.
FastAPI wird jeden Parameter korrekt erkennen und die Daten vom richtigen Ort holen.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: Union[str, None] = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
Die Funktionsparameter werden wie folgt erkannt:
- Wenn der Parameter auch im Pfad deklariert wurde, wird er als Pfad-Parameter interpretiert.
- Wenn der Parameter ein einfacher Typ ist (wie
int
,float
,str
,bool
, usw.), wird er als Query-Parameter interpretiert. - Wenn der Parameter vom Typ eines Pydantic-Modells ist, wird er als Requestbody interpretiert.
Hinweis
FastAPI weiß, dass der Wert von q
nicht erforderlich ist, wegen des definierten Defaultwertes = None
Das Union
in Union[str, None]
wird von FastAPI nicht verwendet, aber es erlaubt Ihrem Editor, Sie besser zu unterstützen und Fehler zu erkennen.
Ohne Pydantic¶
Wenn Sie keine Pydantic-Modelle verwenden wollen, können Sie auch Body-Parameter nehmen. Siehe die Dokumentation unter Body – Mehrere Parameter: Einfache Werte im Body.