How to Use FastCRUD with Pydantic v2 Schemas: Complete Implementation Guide
FastCRUD is built exclusively for Pydantic v2, leveraging the model_dump() API and generic type aliases bound to BaseModel to provide type-safe CRUD operations with SQLAlchemy models.
FastCRUD is a modern Python library designed to accelerate FastAPI development by providing generic CRUD operations that integrate seamlessly with SQLAlchemy and Pydantic v2 schemas. Unlike legacy solutions that rely on Pydantic v1's dict() method, FastCRUD leverages the v2 model_dump() API for robust schema handling. This guide walks you through the architectural patterns, implementation files, and practical code examples needed to implement FastCRUD with Pydantic v2 schemas in your FastAPI applications.
Architectural Overview of FastCRUD with Pydantic v2
FastCRUD's architecture centers on strict Pydantic v2 compliance. The library uses generic type parameters to ensure that all schema inputs and outputs conform to Pydantic v2's BaseModel interface.
Core Type System and Generic Bindings
In fastcrud/types.py, FastCRUD defines generic type aliases that are explicitly bounded to Pydantic v2's BaseModel:
- CreateSchemaType: Used for
create()operations - UpdateSchemaType: Used for
update()operations - SelectSchemaType: Used for
get()andget_multi()return types - DeleteSchemaType: Used for soft-delete configurations
These aliases ensure that any schema passed to FastCRUD methods inherits from pydantic.BaseModel and supports the v2 API.
Schema Serialization Pipeline
When creating or updating records, FastCRUD converts Pydantic v2 schemas to dictionaries using the modern model_dump() method. In fastcrud/crud/fast_crud.py, the create() method calls object.model_dump() (lines 106-108) to extract a plain Python dictionary that can be passed directly to the SQLAlchemy model constructor.
This approach replaces the deprecated Pydantic v1 dict() method and ensures compatibility with Pydantic v2's serialization behavior, including custom serializers and computed fields.
Output Model Instantiation
FastCRUD supports returning Pydantic v2 models directly from database queries via the return_as_model parameter. When return_as_model=True and a schema_to_select is provided, FastCRUD instantiates the schema using the v2 constructor: schema_to_select(**out).
This pattern appears in fastcrud/crud/fast_crud.py within the get() and get_multi() methods (around lines 100-105), allowing you to work with fully-typed Pydantic objects throughout your application layer.
Implementing FastCRUD with Pydantic v2 Schemas in FastAPI
Integrating FastCRUD into a FastAPI application requires defining Pydantic v2 schemas that match your SQLAlchemy models, then wiring them into the generic FastCRUD class.
Defining Pydantic v2 Schemas
Create schemas that inherit from pydantic.BaseModel and use Pydantic v2 configuration patterns. The fastcrud/examples/user/schemas.py file demonstrates the expected structure:
from pydantic import BaseModel, ConfigDict
from datetime import datetime
from typing import Optional
class CreateUserSchema(BaseModel):
name: str
email: str
is_active: bool = True
class ReadUserSchema(BaseModel):
id: int
name: str
email: str
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class UpdateUserSchema(BaseModel):
name: Optional[str] = None
email: Optional[str] = None
class DeleteUserSchema(BaseModel):
pass
The model_config = ConfigDict(from_attributes=True) setting enables ORM mode, allowing Pydantic to read attributes from SQLAlchemy model instances when converting query results to schemas.
Configuring the FastCRUD Instance
Instantiate FastCRUD with generic type parameters that map your SQLAlchemy model to its corresponding Pydantic v2 schemas:
from fastcrud import FastCRUD
from fastcrud.examples.user.model import User # SQLAlchemy model
from fastcrud.examples.user.schemas import (
CreateUserSchema,
ReadUserSchema,
UpdateUserSchema,
DeleteUserSchema,
)
# Generic signature: Model, CreateSchema, UpdateSchema, SelectSchema, DeleteSchema
UserCRUD = FastCRUD[User, CreateUserSchema, UpdateUserSchema, None, DeleteUserSchema]
user_crud = UserCRUD(User)
The None value for the fourth parameter indicates we will specify the select schema at call time rather than baking it into the instance.
Auto-Generating CRUD Endpoints
Use crud_router from fastcrud/endpoint/crud_router.py to automatically create FastAPI routes that validate requests and responses using your Pydantic v2 schemas:
from fastcrud import crud_router
from fastapi import FastAPI
router = crud_router(
session=get_db, # Your async session dependency
model=User,
create_schema=CreateUserSchema,
read_schema=ReadUserSchema, # Used for GET /users/{id}
update_schema=UpdateUserSchema,
delete_schema=DeleteUserSchema,
path="/users",
tags=["users"],
)
app = FastAPI()
app.include_router(router)
This router automatically handles the conversion between Pydantic v2 schemas and SQLAlchemy models using FastCRUD's internal model_dump() calls.
Complete FastAPI Application Example
Below is a minimal, asynchronous FastAPI application demonstrating the full lifecycle of FastCRUD with Pydantic v2 schemas, including database setup and automatic endpoint generation.
# app.py
import datetime
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from fastcrud import FastCRUD, crud_router
from fastcrud.examples.user.model import User # SQLAlchemy model
from fastcrud.examples.user.schemas import (
CreateUserSchema,
ReadUserSchema,
UpdateUserSchema,
DeleteUserSchema,
)
# -------------------- Database setup --------------------
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
engine = create_async_engine(DATABASE_URL, echo=True)
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)
async def get_db() -> AsyncSession:
async with AsyncSessionLocal() as session:
yield session
# -------------------- FastCRUD instance --------------------
UserCRUD = FastCRUD[User, CreateUserSchema, UpdateUserSchema, None, DeleteUserSchema]
user_crud = UserCRUD(User)
# -------------------- FastAPI router --------------------
router = crud_router(
session=get_db,
model=User,
create_schema=CreateUserSchema,
read_schema=ReadUserSchema,
update_schema=UpdateUserSchema,
delete_schema=DeleteUserSchema,
)
# -------------------- Application --------------------
app = FastAPI()
app.include_router(router, prefix="/users", tags=["users"])
This example demonstrates how FastCRUD handles the entire data flow: Pydantic v2 schemas validate incoming requests, model_dump() converts them to dictionaries for SQLAlchemy, and return_as_model instantiates schemas from database rows.
Advanced Query Patterns with Pydantic v2 Schemas
FastCRUD extends beyond basic CRUD operations by supporting complex filtering, pagination, and relationship handling while maintaining Pydantic v2 compatibility.
Filter Operators and Joined Models
FastCRUD automatically translates filter operators into SQLAlchemy expressions. You can use double-underscore notation to apply operators like __contains, __eq, __gt, and even traverse relationships:
# Filter by substring
users = await user_crud.get_multi(
db,
email__contains="example.com",
return_as_model=True,
schema_to_select=ReadUserSchema,
)
# Filter across relationships (automatic JOIN handling)
items = await item_crud.get_multi(
db,
profile__company__name__eq="Acme",
return_as_model=True,
schema_to_select=ReadItemSchema,
)
This filtering logic is implemented in fastcrud/core/query/builder.py, which parses the filter keys, validates the relationship paths, and constructs the necessary SQLAlchemy JOINs.
Soft Delete Configurations
For applications requiring soft deletes, FastCRUD accepts a DeleteSchemaType that can include audit fields. Define a deletion schema and configure it in your CRUD instance:
from pydantic import BaseModel
from datetime import datetime
class DeleteUserSchema(BaseModel):
deleted_at: datetime
is_deleted: bool = True
# Use in FastCRUD generic parameters
UserCRUD = FastCRUD[User, CreateUserSchema, UpdateUserSchema, None, DeleteUserSchema]
The deletion logic references fastcrud/core/config/crud_configs.py for configuration structures, allowing you to inject timestamps or user IDs during soft-delete operations.
Key Source Files for FastCRUD Pydantic v2 Integration
Understanding the source code helps when debugging schema conversion issues or extending functionality. These files contain the core Pydantic v2 integration points:
-
fastcrud/crud/fast_crud.py– Contains the genericFastCRUDclass implementation. Look here for themodel_dump()calls on lines 106-108 during create operations, and theschema_to_select(**out)instantiation pattern inget()andget_multi()methods around lines 100-105. -
fastcrud/types.py– Defines the generic type aliasesCreateSchemaType,UpdateSchemaType,SelectSchemaType, andDeleteSchemaType(lines 14-18), all explicitly bounded to Pydantic v2'sBaseModel. -
fastcrud/examples/user/schemas.py– Reference implementation showing concrete Pydantic v2 schemas (CreateUserSchema,ReadUserSchema,UpdateUserSchema,DeleteUserSchema) with properConfigDictusage. -
fastcrud/endpoint/crud_router.py– Implements thecrud_routerfunction that automatically generates FastAPI endpoints from your Pydantic v2 schemas and FastCRUD instance. -
fastcrud/core/query/builder.py– Handles the parsing of filter operators (like__contains,__eq) and automatic JOIN construction for relationship filtering.
Summary
FastCRUD provides a type-safe, generic CRUD layer built specifically for Pydantic v2 and SQLAlchemy. Key implementation points include:
-
Generic type binding:
CreateSchemaType,UpdateSchemaType, and related aliases infastcrud/types.pyenforce Pydantic v2BaseModelconstraints at the type level. -
Modern serialization: The library uses
model_dump()(not the deprecateddict()) to convert Pydantic v2 schemas to dictionaries before passing them to SQLAlchemy constructors, as seen infastcrud/crud/fast_crud.py. -
Automatic endpoint generation: The
crud_routerfunction infastcrud/endpoint/crud_router.pyconsumes your Pydantic v2 schemas to generate fully-typed FastAPI endpoints with request validation and response serialization. -
Advanced filtering: Support for Pydantic v2 schemas in filter operations, including relationship traversal via double-underscore notation (
profile__company__name__eq), implemented infastcrud/core/query/builder.py.
Frequently Asked Questions
Does FastCRUD support Pydantic v1?
No. FastCRUD is built exclusively for Pydantic v2. The library relies on the model_dump() method introduced in Pydantic v2 for schema serialization, and all generic type aliases in fastcrud/types.py are explicitly bounded to Pydantic v2's BaseModel. If you attempt to use Pydantic v1 schemas, you will encounter attribute errors when FastCRUD attempts to call model_dump().
How do I enable ORM mode for my Pydantic v2 schemas?
Add a model_config class attribute using ConfigDict with from_attributes=True to your schema definition. This allows Pydantic v2 to read attributes from SQLAlchemy model instances when FastCRUD converts database rows to schema instances via schema_to_select(**out). For example:
from pydantic import BaseModel, ConfigDict
class ReadUserSchema(BaseModel):
id: int
name: str
model_config = ConfigDict(from_attributes=True)
Can I use custom Pydantic v2 validators with FastCRUD?
Yes. FastCRUD passes your Pydantic v2 schemas directly to the SQLAlchemy model constructors after calling model_dump(), but validation occurs when you instantiate the schema itself. You can use Pydantic v2's @field_validator and @model_validator decorators in your CreateSchemaType or UpdateSchemaType classes, and FastCRUD will respect these validations because it receives already-validated schema instances from FastAPI's dependency injection system.
What is the performance impact of using return_as_model?
When you set return_as_model=True and provide a schema_to_select parameter, FastCRUD instantiates Pydantic v2 models for every row returned by the database query. This adds a small serialization overhead compared to returning raw dictionaries, but provides full type safety and IDE autocomplete. For high-throughput endpoints where microseconds matter, set return_as_model=False and work with dictionaries, converting to Pydantic models only at the service layer when necessary.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →