Сериализаторы
Роль сериализатора
Сериализатор делает для API то же, что форма для HTML:
| Направление | Что делает |
|---|---|
| Объект → JSON (чтение) | Выбирает и форматирует поля |
| JSON → объект (запись) | Проверяет данные, создаёт или обновляет объект |
Serializer: всё вручную
from rest_framework import serializers
class PostSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=250)
body = serializers.CharField()
publish = serializers.DateTimeField(read_only=True)
def create(self, validated_data):
return Post.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.title = validated_data.get("title", instance.title)
instance.body = validated_data.get("body", instance.body)
instance.save()
return instance
ModelSerializer: по модели
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ["id", "title", "slug", "author", "body", "publish", "status"]
read_only_fields = ["id", "author", "publish"]
Поля, их типы, валидация (max_length, unique, choices) и методы create()/update() создаются автоматически.
Представление связей
По умолчанию ForeignKey выводится как id. Варианты:
class PostSerializer(serializers.ModelSerializer):
# 1. Одно поле связанного объекта
author = serializers.ReadOnlyField(source="author.username")
# 2. Строковое представление (__str__)
author = serializers.StringRelatedField()
# 3. Слаг вместо id — и для чтения, и для записи
category = serializers.SlugRelatedField(slug_field="slug", queryset=Category.objects.all())
# 4. Ссылка на ресурс
author = serializers.HyperlinkedRelatedField(view_name="api:user_detail", read_only=True)
Теги django-taggit можно вывести как список строк:
tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field="name")
Вложенные сериализаторы
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ["id", "name", "body", "created"]
class PostDetailSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source="author.username")
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ["id", "title", "author", "body", "publish", "comments"]
{
"id": 1,
"title": "Первый пост",
"author": "admin",
"comments": [
{"id": 7, "name": "Анна", "body": "Отличный пост!", "created": "2026-10-25T12:00:00+03:00"}
]
}
Часто делают два сериализатора: короткий для списка и подробный для детальной страницы.
N+1 в API
Вложенные сериализаторы и source="author.username" обращаются к связанным объектам. Не забывайте select_related("author") и prefetch_related("comments") в queryset представления.
Вычисляемые поля
class PostSerializer(serializers.ModelSerializer):
comments_count = serializers.SerializerMethodField()
url = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ["id", "title", "comments_count", "url"]
def get_comments_count(self, obj):
return obj.comments.count()
def get_url(self, obj):
request = self.context.get("request")
return request.build_absolute_uri(obj.get_absolute_url())
Generic-представления DRF сами передают request в self.context.
Валидация
Как в формах, только методы называются иначе:
class PostSerializer(serializers.ModelSerializer):
...
def validate_title(self, value): # одно поле (в формах — clean_title)
if len(value) < 5:
raise serializers.ValidationError("Слишком короткий заголовок")
return value
def validate(self, data): # несколько полей (в формах — clean)
if data.get("status") == Post.Status.PUBLISHED and not data.get("body"):
raise serializers.ValidationError("Нельзя опубликовать пустой пост")
return data
Ошибки возвращаются клиенту с кодом 400:
{"title": ["Слишком короткий заголовок"]}
Сохранение
serializer = PostSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # при ошибке DRF вернёт 400
post = serializer.save(author=request.user) # дополнительные поля передаются в save()
serializer = PostSerializer(post, data=request.data, partial=True) # обновление (PATCH)
save(author=request.user) — аналог form.save(commit=False) + post.author = ....
Итог
ModelSerializerстроит поля и валидацию по модели;read_only_fields— поля только для чтения.- Связи:
ReadOnlyField(source=...),StringRelatedField,SlugRelatedField, вложенные сериализаторы. SerializerMethodField+get_<поле>— вычисляемые значения.- Валидация:
validate_<поле>иvalidate; сохранение:is_valid()→save(**доп_поля).
Практика решено 0 из 6
В PostSerializer выведите автора по имени пользователя вместо id: объявите поле
author = serializers.ReadOnlyField(source="author.username").
class PostSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source="author.username")
class Meta:
model = Post
fields = ["id", "title", "author", "body", "publish"]
Добавьте в сериализатор вычисляемое поле comments_count через SerializerMethodField
и метод get_comments_count, возвращающий количество комментариев поста (obj.comments.count()).
class PostSerializer(serializers.ModelSerializer):
comments_count = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ["id", "title", "comments_count"]
def get_comments_count(self, obj):
return obj.comments.count()
Добавьте в сериализатор проверку поля title: если длина меньше 5 символов, выбросьте
serializers.ValidationError("Слишком короткий заголовок"), иначе верните значение.
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ["id", "title", "body"]
def validate_title(self, value):
if len(value) < 5:
raise serializers.ValidationError("Слишком короткий заголовок")
return value
Как вывести у поста список комментариев с их полями (name, body), а не только id?
Расставьте шаги создания объекта через сериализатор.
-
return Response(serializer.data, status=201) -
serializer.save(author=request.user) -
serializer.is_valid(raise_exception=True) -
serializer = PostSerializer(data=request.data)
Какие поля сериализатора только для чтения (не принимаются при записи)?
Войдите, чтобы отмечать пройденные уроки.