Unfazed Serializer¶
Unfazed's Serializer automatically generates a Pydantic model from a Tortoise ORM model and provides built-in async CRUD operations — create, retrieve, update, delete, and paginated list. It bridges the gap between your database models and your API layer, giving you validated data objects with zero boilerplate.
Quick Start¶
1. Define a Tortoise model¶
# myapp/models.py
from tortoise import Model, fields
class Article(Model):
title = fields.CharField(max_length=255)
content = fields.TextField()
published = fields.BooleanField(default=False)
created_at = fields.DatetimeField(auto_now_add=True)
2. Create a serializer¶
# myapp/serializers.py
from unfazed.serializer import Serializer
from myapp.models import Article
class ArticleSerializer(Serializer):
class Meta:
model = Article
ArticleSerializer now has Pydantic fields for id, title, content, published, and created_at — auto-generated from the model.
3. Use CRUD operations¶
from pydantic import BaseModel
from myapp.serializers import ArticleSerializer
class CreateArticle(BaseModel):
title: str
content: str
class UpdateArticle(BaseModel):
id: int
title: str
# Create
article = await ArticleSerializer.create_from_ctx(
CreateArticle(title="Hello", content="World")
)
# Retrieve
class ArticleId(BaseModel):
id: int
article = await ArticleSerializer.retrieve_from_ctx(ArticleId(id=1))
# Update
updated = await ArticleSerializer.update_from_ctx(
UpdateArticle(id=1, title="Updated Title")
)
# Delete
await ArticleSerializer.destroy_from_ctx(ArticleId(id=1))
# List with pagination
result = await ArticleSerializer.list_from_ctx(
cond={"published": True},
page=1,
size=20,
)
# result.count — total matching records
# result.data — list of ArticleSerializer instances
The Meta Class¶
Every serializer must define an inner Meta class with at least a model attribute:
Meta options¶
| Option | Type | Default | Description |
|---|---|---|---|
model |
Type[Model] |
required | The Tortoise ORM model to serialize. |
include |
List[str] |
[] |
Fields to include. If set, all other fields are excluded. |
exclude |
List[str] |
[] |
Fields to exclude. Cannot be used together with include. |
enable_relations |
bool |
False |
Generate fields for relation models (FK, M2M, O2O). |
Controlling fields¶
By default all model fields are included. Use include or exclude (but not both) to control which fields appear:
class ArticleSummarySerializer(Serializer):
class Meta:
model = Article
include = ["id", "title", "published"]
class ArticleSerializer(Serializer):
class Meta:
model = Article
exclude = ["created_at"]
CRUD Operations¶
All CRUD methods are class methods that accept a Pydantic BaseModel context object. The context provides the data for the operation.
create_from_ctx¶
Creates a new database record from ctx and returns the serialized instance.
retrieve_from_ctx¶
Retrieves a record by ctx.id and returns the serialized instance.
update_from_ctx¶
Finds the record by ctx.id, updates the fields present in ctx, saves, and returns the serialized instance.
destroy_from_ctx¶
Finds the record by ctx.id and deletes it.
list_from_ctx¶
Queries records matching cond (a dict of filter kwargs passed to Tortoise's .filter()), paginates the results, and returns a Result object.
| Parameter | Type | Description |
|---|---|---|
cond |
Dict |
Tortoise ORM filter kwargs (e.g. {"published": True}). |
page |
int |
Page number (1-based). Pass 0 to disable pagination. |
size |
int |
Page size. |
order_by |
str \| List[str] |
Optional ordering field(s). Prefix with - for descending. |
fetch_relations |
bool |
Whether to prefetch related objects. Defaults to True. |
Returns a Result[T] with .count (total) and .data (list of serializer instances).
list_from_queryset¶
You can also pass a pre-built QuerySet instead of a condition dict:
qs = Article.filter(published=True).order_by("-created_at")
result = await ArticleSerializer.list_from_queryset(qs, page=1, size=10)
Relation Support¶
Set enable_relations = True in Meta to include relation fields (ForeignKey, ManyToMany, OneToOne, and their backward variants):
Important: Relations are only resolved after Tortoise.init() has been called. Avoid accessing serializers with enable_relations = True inside app.ready(). If you need early access, either override the relation field manually or exclude it:
# Option 1: Override with a default
class StudentSerializer(Serializer):
class Meta:
model = Student
enable_relations = True
courses: list = []
# Option 2: Exclude the relation
class StudentSerializer(Serializer):
class Meta:
model = Student
exclude = ["courses"]
find_relation¶
Discover the relationship between two serializers:
relation = StudentSerializer.find_relation(CourseSerializer)
# Relation(target="CourseSerializer", source_field="...", target_field="...", relation="m2m")
Returns a Relation object or None if no relationship exists. Supported relation types: fk, m2m, o2o, bk_fk, bk_o2o.
Custom Fields¶
You can add or override fields on a serializer:
from pydantic import Field
from unfazed.serializer import Serializer
class CarSerializer(Serializer):
override: int = 100
computed: str = Field(default="custom_value")
class Meta:
model = Car
Custom fields are merged with the auto-generated fields. If a custom field has the same name as a model field, the custom definition takes precedence.
API Reference¶
Serializer¶
Base class for all serializers. Automatically generates Pydantic fields from the Tortoise model defined in Meta.
Class methods (CRUD):
async create_from_ctx(ctx: BaseModel, **kwargs) -> Self: Create a record.async update_from_ctx(ctx: BaseModel, **kwargs) -> Self: Update a record (looks up byctx.id).async destroy_from_ctx(ctx: BaseModel, **kwargs) -> None: Delete a record (looks up byctx.id).async retrieve_from_ctx(ctx: BaseModel, **kwargs) -> Self: Retrieve a single record.async list_from_ctx(cond: Dict, page: int, size: int, **kwargs) -> Result[Self]: List with pagination.async list_from_queryset(queryset: QuerySet, page: int, size: int, **kwargs) -> Result[Self]: List from a pre-built queryset.
Class methods (low-level):
from_instance(instance: Model) -> Self: Create a serializer from a model instance.find_relation(other_cls: Type[Serializer]) -> Relation | None: Discover relationship between two serializers.get_queryset(cond: Dict, **kwargs) -> QuerySet: Build a queryset from conditions.get_fetch_fields() -> List[str]: Return relation fields to prefetch.
Instance methods:
async create(**kwargs) -> Model: Insert the serializer's data into the database.async update(instance: Model, **kwargs) -> Model: Update an existing model instance.async retrieve(instance: Model, **kwargs) -> Self(classmethod): Fetch relations and return serialized instance.async destroy(instance: Model, **kwargs) -> None(classmethod): Delete a model instance.valid_data -> Dict: The subset of fields that can be written to the database.get_write_fields() -> List[str]: Return writable (non-pk, non-generated, non-auto) field names.
Result¶
Pagination result returned by list_from_ctx and list_from_queryset.
Relation¶
class Relation(BaseModel):
target: str
source_field: str
target_field: str
relation: Literal["fk", "m2m", "o2o", "bk_fk", "bk_o2o"]
Describes a relationship between two serializers.