Представьте, что у вас есть две модели в базе данных: User и Profile. Пользователь может иметь один профиль (связь OneToOne). Если вы хотите вернуть данные пользователя вместе с данными его профиля в одном запросе, то вам нужен вложенный сериализатор. Он помогает включить поля связанных моделей в сериализованный результат.
Аналогия: представьте матрёшку — главная внешняя кукла содержит внутри ещё одну куклу, которая, в свою очередь, может содержать другую. Вот так и работают вложенные сериализаторы: один сериализатор может "вкладывать" в себя другой.
Практика: создание вложенных сериализаторов
Давайте создадим модели Author и Book, где каждый автор может написать несколько книг (связь ForeignKey). Наша цель — вернуть данные автора, включая список его книг.
from django.db import models
# Модель автора
class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
def __str__(self):
return self.name
# Модель книги
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)
def __str__(self):
return self.title
Обратите внимание на related_name='books' в поле author. Это упрощает доступ к книгам автора через обратную связь: например, author.books.all().
Создание сериализаторов
Теперь мы создадим два сериализатора: один для объекта книги BookSerializer, а другой для автора AuthorSerializer, который включает вложенный список книг.
from rest_framework import serializers
from .models import Author, Book
# Сериализатор для книги
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'title']
# Сериализатор для автора
class AuthorSerializer(serializers.ModelSerializer):
books = BookSerializer(many=True) # Вложенный сериализатор
class Meta:
model = Author
fields = ['id', 'name', 'email', 'books']
- Мы создали
BookSerializerдля сериализации данных о книгах. - В
AuthorSerializerмы добавили полеbooks, которое содержит список вложенных объектов. Параметрmany=Trueуказывает, что поле содержит массив данных.
Пример использования
Для теста создадим нескольких авторов и их книги:
from myapp.models import Author, Book
# Создаем автора
author = Author.objects.create(name="Isaac Asimov", email="asimov@example.com")
# Создаем книги для автора
Book.objects.create(title="Foundation", author=author)
Book.objects.create(title="I, Robot", author=author)
Теперь воспользуемся AuthorSerializer, чтобы посмотреть, как выглядят данные:
from myapp.serializers import AuthorSerializer
from myapp.models import Author
author = Author.objects.get(name="Isaac Asimov")
serializer = AuthorSerializer(author)
print(serializer.data)
Результат:
{
"id": 1,
"name": "Isaac Asimov",
"email": "asimov@example.com",
"books": [
{
"id": 1,
"title": "Foundation"
},
{
"id": 2,
"title": "I, Robot"
}
]
}
Изменение вложенных данных
Вложенные сериализаторы позволяют не только отображать данные, но и создавать или обновлять связанные объекты.
Представим, что вы хотите добавить новую книгу через сериализатор. Для этого потребуется переопределить метод create().
class AuthorSerializer(serializers.ModelSerializer):
books = BookSerializer(many=True)
class Meta:
model = Author
fields = ['id', 'name', 'email', 'books']
def create(self, validated_data):
books_data = validated_data.pop('books') # Убираем вложенные данные
author = Author.objects.create(**validated_data) # Создаем автора
for book_data in books_data:
Book.objects.create(author=author, **book_data) # Создаем книги
return author
Пример запроса на создание:
{
"name": "George Orwell",
"email": "orwell@example.com",
"books": [
{"title": "1984"},
{"title": "Animal Farm"}
]
}
Результат: новый автор и его книги будут добавлены в базу.
Особенности работы с вложенными сериализаторами
Иногда вы можете захотеть сделать вложенные данные только для чтения. Для этого используйте параметр read_only=True:
class AuthorSerializer(serializers.ModelSerializer):
books = BookSerializer(many=True, read_only=True)
class Meta:
model = Author
fields = ['id', 'name', 'email', 'books']
Теперь книги не будут изменяться при использовании сериализатора для создания или обновления данных.
Производительность при работе с вложенными сериализаторами
Если вы сериализуете большое количество объектов с вложенными данными, это может привести к многочисленным запросам к базе данных. Чтобы избежать этого, используйте select_related() и prefetch_related():
authors = Author.objects.prefetch_related('books').all()
serializer = AuthorSerializer(authors, many=True)
Это значительно уменьшит количество запросов, которые делает ваше приложение, и ускорит работу.
Ошибка: "Got a TypeError: Object of type 'Book' is not JSON serializable".
Если вы забыли, что Serializer нужен для преобразования моделей в сериализуемые форматы, вы можете столкнуться с подобной ошибкой. Убедитесь, что все вложенные объекты обрабатываются через сериализаторы.
Рекомендации для работы
Когда использовать вложенные сериализаторы? Есть несколько основных кейсов:
- Простые зависимости: когда необходимо показать связанные данные простым списком или объектом.
- Чтение данных: когда вам нужно показать связанные данные клиенту.
- Небольшие объемы данных: вложенные сериализаторы подходят для обработки небольших объемов данных. Для более сложных сценариев может потребоваться разделение запросов.
В реальной работе:
- Используйте вложенные сериализаторы для чтения данных, но избегайте сложной логики создания/обновления без необходимости.
- Основные сценарии обновлений лучше обрабатывать на уровне представлений (views).
- Оптимизируйте запросы с помощью
select_related()иprefetch_related()для минимизации нагрузки на базу данных.
Теперь, когда вы знаете, как использовать вложенные сериализаторы, давайте добавим их в наш проект и начнем экспериментировать.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ