Как в Django REST Framework сериализовать связанные модели или добавить вычисляемые поля

«Как в Django REST Framework сериализовать связанные модели или добавить вычисляемые поля» — вопрос из категории Django, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для работы со сложными структурами данных в Django REST Framework (DRF) используются несколько подходов:

  1. Вложенные сериализаторы (Nested Serializers): Позволяют включать представления связанных моделей (OneToOne, ForeignKey, ManyToMany) в родительский объект.

  2. SerializerMethodField: Используется для добавления в сериализатор вычисляемых полей, значение которых определяется методом самого сериализатора. Это идеально подходит для агрегаций, кастомной логики или данных, не хранящихся в модели напрямую.

  3. Переопределение методов .create() и .update(): Необходимо для реализации записи во вложенные сериализаторы, так как DRF по умолчанию не умеет обрабатывать создание/обновление связанных объектов.

Пример: Сериализатор профиля пользователя, который включает данные самого пользователя и вычисляет количество его постов.

from rest_framework import serializers
from django.contrib.auth.models import User

# Сериализатор для вложенной модели User
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email']

class ProfileSerializer(serializers.ModelSerializer):
    # 1. Вложенный сериализатор (только для чтения по умолчанию)
    user = UserSerializer()

    # 2. Вычисляемое поле
    posts_count = serializers.SerializerMethodField()

    class Meta:
        model = Profile
        fields = ['id', 'user', 'bio', 'posts_count']

    def get_posts_count(self, obj):
        # obj - это экземпляр модели Profile
        return obj.posts.count()

    # 3. Переопределение create для поддержки записи вложенных данных
    def create(self, validated_data):
        user_data = validated_data.pop('user')
        user = User.objects.create_user(**user_data)
        profile = Profile.objects.create(user=user, **validated_data)
        return profile

Оптимизация: Для предотвращения проблемы N+1 при работе с такими сериализаторами в ViewSet следует использовать select_related (для ForeignKey) и prefetch_related (для ManyToMany) в queryset.