Models
Using models for data validation is one of FastAPI's key features, leveraging the power of Pydantic to ensure the integrity and correctness of the data being processed by your API. Here’s a detailed guide on how to use models in FastAPI for data validation:
1. Basic Model Definition with Pydantic
Pydantic models are Python classes that define the schema of your data. Here’s how to create and use a Pydantic model:
Define a Pydantic Model
Create a file models.py
(or directly in main.py
for simple cases), and define your model:
# models.py
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None # Optional field
price: float
tax: float = None # Optional field
BaseModel
is the base class provided by Pydantic.- Fields are defined as class attributes with their types.
- Optional fields can have default values (e.g.,
description: str = None
).
Use the Model in Request Body
In your FastAPI routes, you can use the model to validate the request body:
# main.py
from fastapi import FastAPI
from models import Item
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return {"item": item}
- FastAPI will automatically parse and validate the incoming JSON data against the
Item
model.
2. Using Models for Path and Query Parameters
You can also use Pydantic models to validate path and query parameters.
Path Parameters
Define a model for path parameters if they have a complex structure:
# models.py
class User(BaseModel):
id: int
name: str
Use it in a route:
# main.py
from models import User
@app.get("/users/{user_id}")
async def read_user(user_id: int):
return {"user_id": user_id}
Query Parameters
You can pass query parameters directly, but for complex validation, use a Pydantic model:
# main.py
from pydantic import BaseModel
from fastapi import Depends
class QueryParams(BaseModel):
q: str = None
limit: int = 10
offset: int = 0
@app.get("/items/")
async def read_items(params: QueryParams = Depends()):
return {"query": params.q, "limit": params.limit, "offset": params.offset}
Depends()
is used to declare dependencies, allowing FastAPI to extract and validate query parameters based on theQueryParams
model.
3. Validation and Default Values
Pydantic provides extensive validation and default value capabilities.
Field Validations
You can use Pydantic's validators to enforce constraints:
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str
price: float = Field(..., gt=0) # Price must be greater than 0
quantity: int = Field(default=1, ge=1) # Quantity must be at least 1
Field
allows specifying additional validations and metadata.gt=0
specifies thatprice
must be greater than 0.ge=1
specifies thatquantity
must be greater than or equal to 1.
Nested Models
You can nest models for complex data structures:
class Manufacturer(BaseModel):
name: str
country: str
class Item(BaseModel):
name: str
price: float
manufacturer: Manufacturer
- The
manufacturer
field is an instance of theManufacturer
model.
4. Using Models for Response Validation
FastAPI also supports response model validation, ensuring that the response data matches the expected schema.
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item # The response will be validated against the Item model
response_model
specifies the model that the response should conform to.
5. Extending and Customizing Validation
Custom Validation
You can define custom validation logic using Pydantic's validators:
from pydantic import BaseModel, validator
class Item(BaseModel):
name: str
price: float
quantity: int
@validator('name')
def name_must_be_non_empty(cls, value):
if not value:
raise ValueError('Name must not be empty')
return value
@validator('price')
def price_must_be_positive(cls, value):
if value <= 0:
raise ValueError('Price must be greater than 0')
return value
- Use the
@validator
decorator to add custom validation logic.
Example Project with Data Validation
Here's a complete example that incorporates these concepts:
Project Structure
fastapi_project/
│
├── main.py
├── models.py
└── requirements.txt
models.py
from pydantic import BaseModel, Field
class Manufacturer(BaseModel):
name: str
country: str
class Item(BaseModel):
name: str
description: str = None
price: float = Field(..., gt=0, description="Price must be greater than zero")
tax: float = None
manufacturer: Manufacturer
main.py
from fastapi import FastAPI, HTTPException
from models import Item, Manufacturer
app = FastAPI()
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
if item.price <= 0:
raise HTTPException(status_code=400, detail="Price must be positive")
return item
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id, "name": "Sample Item"}
@app.get("/manufacturers/", response_model=Manufacturer)
async def read_manufacturer():
return {"name": "FastAPI Corp", "country": "USA"}
Conclusion
FastAPI's integration with Pydantic provides a powerful and easy-to-use system for data validation. By defining models, you can ensure that the data your API handles is well-structured and adheres to the expected schema, simplifying the development process and reducing the likelihood of errors.