JavaRush /Курсы /Модуль 3: Django /Сериализация связанных объектов в Django REST Framework

Сериализация связанных объектов в Django REST Framework

Модуль 3: Django
17 уровень , 5 лекция
Открыта

Пришло время углубиться в тему сериализации в Django REST Framework (DRF) и научиться сереализовать связанные объекты между моделями. Если вы когда-то задавались вопросом, как при запросе получить сразу всю нужную структуру данных — от связей ForeignKey до хитрых ManyToMany, то вы попали по адресу.

Сегодня мы разберём, как работать со связанными объектами в Django ORM при помощи DRF. Вы узнаете:

  1. Как сериализовать связанные объекты ForeignKey, OneToOne, ManyToMany и включать их в ответ.
  2. Как использовать related_name для настройки сериализаторов.
  3. Примеры сериализации связанных объектов через ModelSerializer.
  4. Как кастомизировать сериализацию связанных данных.

Немного теории: что такое связанные объекты?

В Django модели могут быть связаны друг с другом через специальные типы отношений:

  • ForeignKey (один-ко-многим): пример — у книги есть один автор, но автор может написать множество книг.
  • OneToOneField (один-к-одному): пример — у каждого пользователя может быть только один профиль, и наоборот.
  • ManyToManyField (многие-ко-многим): пример — одна книга может быть переведена на множество языков, один язык может принадлежать многим книгам.

Эти связи важно учитывать при проектировании базы данных, ведь в реальном мире данные всегда оказываются взаимосвязанными. Например, когда вы запрашиваете список книг, вам может потребоваться сразу получить информацию об их авторах.

Пример реального сценария

Представим, что мы разрабатываем простое API для книжного магазина. У нас есть две модели:

  • Автор (Author): содержит имя и дату рождения автора.
  • Книга (Book): содержит название книги, описание и связь с автором через ForeignKey.

Модели

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    birth_date = models.DateField()

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField()
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')

    def __str__(self):
        return self.title
  • У каждой книги есть только один автор, поэтому мы используем связь ForeignKey.
  • Параметр related_name='books' позволяет нам обращаться к книгам автора через author.books.

При запросе списка книг, мы хотим получить данные примерно в таком формате:

[
    {
        "title": "Война и мир",
        "description": "Эпический роман...",
        "author": {
            "name": "Лев Толстой",
            "birth_date": "1828-09-09"
        }
    },
    {
        "title": "Преступление и наказание",
        "description": "Роман о психологии преступления...",
        "author": {
            "name": "Фёдор Достоевский",
            "birth_date": "1821-11-11"
        }
    }
]

Базовая сериализация связанных объектов

Для начала создадим простой ModelSerializer для модели Author:

from rest_framework import serializers

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ['name', 'birth_date']

Этот сериализатор преобразует объект автора в плоский JSON с указанием имени и даты рождения.

Теперь создадим сериализатор для книги, но добавим в него вложенный сериализатор для отображения данных автора:

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(read_only=True)

    class Meta:
        model = Book
        fields = ['title', 'description', 'author']

Что мы сделали?

  1. Добавили поле author, которое использует наш AuthorSerializer для вложенной сериализации.
  2. Указали, что это поле read_only=True, так как в этом примере мы не хотим изменять данные автора через сериализатор книги (по крайней мере пока).

Теперь, при запросе списка книг, мы получим информацию об авторе в читаемом JSON-формате.

Сериализация ManyToMany

Давайте добавим к нашим моделям связь ManyToMany. Предположим, у книги есть несколько жанров:

class Genre(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField()
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
    genres = models.ManyToManyField(Genre, related_name='books')

Теперь каждая книга может принадлежать нескольким жанрам, а жанр может быть связан с несколькими книгами.

class GenreSerializer(serializers.ModelSerializer):
    class Meta:
        model = Genre
        fields = ['name']

Добавляем жанры в наш BookSerializer:

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(read_only=True)
    genres = GenreSerializer(many=True, read_only=True)

    class Meta:
        model = Book
        fields = ['title', 'description', 'author', 'genres']
  • Параметр many=True говорит DRF, что поле genres содержит несколько объектов, и их всех нужно сериализовать с помощью GenreSerializer.

Теперь, при запросе списка книг, мы получим такую структуру данных:

[
    {
        "title": "Гарри Поттер",
        "description": "Магическая история...",
        "author": {
            "name": "Дж.К. Роулинг",
            "birth_date": "1965-07-31"
        },
        "genres": [
            {
                "name": "Фэнтези"
            },
            {
                "name": "Приключения"
            }
        ]
    }
]

Использование related_name

Важно понимать, как related_name упрощает доступ к связанным данным.

Допустим, мы хотим вместо списка книг выводить авторов вместе с их книгами. Для этого нам нужно указать related_name в модели Book:

author.books.all()

Если мы хотим отобразить информацию об авторе вместе с их книгами:

class AuthorWithBooksSerializer(serializers.ModelSerializer):
    books = BookSerializer(many=True, read_only=True)

    class Meta:
        model = Author
        fields = ['name', 'birth_date', 'books']

Советы по работе с сериализацией связанных объектов

  • Оптимизация запросов: используйте select_related и prefetch_related в представлениях, чтобы сократить количество запросов к базе данных.
  • Кастомизация JSON-ответа: если вам нужно изменить структуру выходных данных, переопределяйте метод to_representation в сериализаторе.
  • Минимизация вложенности: избегайте слишком глубоких вложенных структур, так как это может привести к перегрузке данных для клиента.

Типичные ошибки

  • Не указывают related_name в модели, что приводит к путанице и невозможности явно определить связи.
  • Пытаются сериализовать объекты, не указывая many=True для полей, содержащих несколько объектов.
  • Забывают использовать read_only=True для связанных данных, которые не должны обновляться через текущий сериализатор.

Теперь вы готовы использовать всю мощь Django REST Framework для работы с сериализацией связанных объектов! 🎉

1
Задача
Модуль 3: Django, 17 уровень, 5 лекция
Недоступна
Сериализация ForeignKey связи
Сериализация ForeignKey связи
1
Задача
Модуль 3: Django, 17 уровень, 5 лекция
Недоступна
Сериализация ManyToMany связи
Сериализация ManyToMany связи
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ