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 для роботи з серіалізацією пов'язаних об'єктів! 🎉

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ