Hello! Welcome back to our journey into FastAPI and Pydantic.
In the last two lessons, we covered how Pydantic models act as FastAPI's equivalent to Django REST Framework serializers for defining data schemas. We started with basic models and then learned to apply fine-grained validation rules using Field() and specialized types like EmailStr.
So far, all our examples have used "flat" JSON objects. However, real-world APIs often need to handle more complex, hierarchical data. For example, a User object might contain a nested Address object, or a BlogPost might have a list of nested Comment objects.
In this lesson, we will build on your knowledge to handle these scenarios. You will learn to create nested Pydantic models to represent complex, hierarchical JSON structures. This concept is a direct parallel to using nested serializers in DRF, so your existing experience will be very helpful here.
1. The Core Concept: Models Within Models
In DRF, you can nest serializers by declaring one serializer as a field inside another. Pydantic works in exactly the same way: you can use a Pydantic BaseModel as the type for a field within another BaseModel.
This allows you to define complex JSON "objects" with specific attribute names, types, and validations, all arbitrarily nested. Let's start with the official FastAPI documentation, which provides a very clear introduction to this idea.
The official FastAPI documentation provides a concise and clear explanation of how to define and use nested models. Pay attention to how the Image model is used as a type within the Item model.
Read the sections titled 'Nested Models', 'Define a submodel', and 'Use the submodel as a type'. The code examples clearly illustrate how one Pydantic model can be an attribute of another.
2. The Analogy to DRF Nested Serializers
The pattern you just saw should feel very familiar. Let's make the connection to Django Rest Framework explicit.
In DRF, you would write:
# Django Rest Framework Example
from rest_framework import serializers
class AddressSerializer(serializers.Serializer):
street = serializers.CharField(max_length=100)
city = serializers.CharField(max_length=50)
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=100)
# The AddressSerializer is nested here
address = AddressSerializer()
In Pydantic, the equivalent is:
# Pydantic / FastAPI Equivalent
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
class User(BaseModel):
username: str
# The Address model is nested here
address: Address
As you can see, the concept is identical. By defining one model (Address) and then using it as a type annotation in another (User), you instruct Pydantic (and by extension, FastAPI) to expect a nested JSON object.
This is what a valid JSON payload for the User model would look like. Notice the address field contains a JSON object, not a simple string or number.

3. Nesting a List of Models
You can take this a step further. What if a field should contain a list of nested objects? For example, an e-commerce order might contain a list of products.
To achieve this, you simply use Python's generic types like list or set. For a list of products, the type hint would be list[Product], where Product is another Pydantic BaseModel.
The following video provides an excellent, practical walkthrough of creating a BlogPost model that contains a nested User object for the author and a list[Comment] for the comments.
Python Pydantic Tutorial: Complete Data Validation Course (Used by FastAPI)
This segment from Corey Schafer's comprehensive Pydantic course demonstrates exactly how to nest both single models and lists of models.
Watch the video from 01:06:12 to 01:11:56. Focus on: How the author field in BlogPost is typed as User. How the comments field is typed as list[Comment]. The structure of the post_data dictionary, which shows what the corresponding JSON would look like. The fact that Pydantic validates everything recursively.
This recursive validation is a powerful feature. If you send a request with a list of 10 comments and one of them has an invalid field, Pydantic will pinpoint the exact error in that specific comment.
4. Using Nested Models in a FastAPI Endpoint
Now, let's bring this back into the context of a live FastAPI application. When you use a Pydantic model with nested structures as a type hint in a path operation, FastAPI handles everything for you automatically:
- Validation: It validates the entire incoming JSON body against your nested models.
- Data Conversion: It converts the raw JSON into Pydantic model instances you can work with in your code.
- Documentation: It generates a rich, interactive schema in the Swagger UI that shows the exact nested structure your API expects.
This next video from BugBytes demonstrates this perfectly. It shows refactoring a simple model into multiple, nested schemas and using them in a POST endpoint.
FastAPI - Request Body and POST requests | Pydantic pre-validators
Watch how a nested Pydantic model is used to create a new resource in a FastAPI endpoint and how this is reflected in the API documentation.
Watch the following clips: 4:35 - 5:30: See how the BandCreate model (which contains a list of AlbumModels) is used as the type hint for the request body in a create_band function. 10:09 - 11:05: Observe a request being sent with nested JSON data for the albums, and see how FastAPI processes it correctly. 11:05 - 12:21: Pay close attention to how the interactive API docs (Swagger UI) automatically display the expected nested schema for both the request and the response.
The automatic documentation is a major advantage. It provides a clear contract for your API consumers, showing them exactly how to structure complex JSON payloads.
5. Deeply Nested Models
There is no limit to how deeply you can nest models. An Offer can contain a list of Items, and each Item can contain a list of Images. Pydantic will handle the validation all the way down.
To see an example of this, let's briefly return to the FastAPI documentation.
This final reading shows how you can combine these concepts to create arbitrarily deep data structures.
Quickly review the sections 'Attributes with lists of submodels' and 'Deeply nested models'. Notice how the Offer model contains list[Item], and Item in turn contains list[Image], creating a multi-level nested structure.
Conclusion
You now have the tools to model and validate complex, hierarchical data structures, a crucial skill for building robust APIs.
Key Takeaways:
- Nesting is achieved through type hints: A field in one
BaseModelcan be typed as anotherBaseModel(for a nested object) orlist[AnotherBaseModel](for a list of nested objects). - The concept is analogous to DRF's nested serializers, making it a familiar pattern.
- FastAPI provides automatic recursive validation, ensuring data integrity throughout the entire JSON structure.
- The interactive API documentation automatically reflects the nested schema, providing clear instructions for API clients.
In our lessons so far, we've relied on Pydantic's built-in validation rules and types. But what if you need to implement custom logic that goes beyond what Field() or types like EmailStr can offer? For instance, what if you need to validate that one field's value depends on another field's value (e.g., password_confirm must match password)?
In our next lesson, we will address this by learning how to implement custom validation logic with @field_validator and @model_validator, which are the Pydantic equivalents of DRF's validate_<field> and validate methods.