Spaces:
Running
Running
File size: 4,524 Bytes
a152b95 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | # Response Model in FastAPI
The `response_model` parameter on route decorators lets you declare the shape of the data your endpoint returns. FastAPI uses it to validate, serialize, and document the response -- filtering out any fields not defined in the model and generating accurate OpenAPI schemas.
## Basic Response Model
```python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserIn(BaseModel):
username: str
email: str
password: str
class UserOut(BaseModel):
username: str
email: str
@app.post("/users/", response_model=UserOut, status_code=201)
async def create_user(user: UserIn):
# In a real app, hash the password and save to DB
return user # password is automatically filtered out
```
Even though the handler returns the full `UserIn` object (which includes `password`), the `response_model=UserOut` declaration ensures that only `username` and `email` appear in the response. This is a critical security pattern -- it prevents accidental leakage of sensitive fields like passwords, tokens, or internal IDs.
## Status Codes
FastAPI provides the `status_code` parameter to set the HTTP response status code. Common codes include:
| Code | Constant | Usage |
|------|------------------------------------|----------------------|
| 200 | `status.HTTP_200_OK` | Successful GET |
| 201 | `status.HTTP_201_CREATED` | Successful creation |
| 204 | `status.HTTP_204_NO_CONTENT` | Successful deletion |
| 400 | `status.HTTP_400_BAD_REQUEST` | Client error |
| 404 | `status.HTTP_404_NOT_FOUND` | Resource not found |
| 422 | `status.HTTP_422_UNPROCESSABLE_ENTITY` | Validation error |
```python
from fastapi import status
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int):
# delete logic
return None
```
The default `status_code` for all route decorators is `200`.
## Filtering Fields with response_model_include and response_model_exclude
You can dynamically control which fields appear in the response without creating a separate model:
```python
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 0.0
internal_code: str = "N/A"
@app.get(
"/items/{item_id}",
response_model=Item,
response_model_exclude={"internal_code"},
)
async def read_item(item_id: int):
return {
"name": "Widget",
"description": "A useful widget",
"price": 35.99,
"tax": 3.60,
"internal_code": "WDG-001",
}
```
The `response_model_exclude` parameter accepts a `set` of field names to strip from the output. Similarly, `response_model_include` accepts a `set` of field names to keep -- all others are excluded. If both are provided, `response_model_include` is applied first, then `response_model_exclude` removes fields from that subset.
## Excluding Unset and Default Values
Two additional parameters control whether default or unset values appear in the response:
```python
@app.get(
"/items/{item_id}",
response_model=Item,
response_model_exclude_unset=True,
)
async def read_item(item_id: int):
return Item(name="Widget", price=35.99)
# Response: {"name": "Widget", "price": 35.99}
# Fields with defaults (description, tax) are omitted
```
- `response_model_exclude_unset=True` -- omits fields the user did not explicitly set (default: `False`)
- `response_model_exclude_defaults=True` -- omits fields whose value matches the default (default: `False`)
- `response_model_exclude_none=True` -- omits fields with `None` values (default: `False`)
## Multiple Response Models
Use `Union` types or the `responses` parameter to document endpoints that may return different shapes:
```python
from typing import Union
class ItemPublic(BaseModel):
name: str
price: float
class ItemAdmin(BaseModel):
name: str
price: float
internal_code: str
profit_margin: float
@app.get("/items/{item_id}", response_model=Union[ItemAdmin, ItemPublic])
async def read_item(item_id: int, is_admin: bool = False):
item_data = get_item(item_id)
if is_admin:
return ItemAdmin(**item_data)
return ItemPublic(**item_data)
```
When using `Union`, Pydantic validates the response against each model in order and uses the first match. Place the more specific model first (the one with more fields) to avoid premature matching.
|