Настав час заглибитися в тему серіалізації у Django REST Framework (DRF) і навчитися серіалізувати пов'язані об'єкти між моделями. Якщо ви коли-небудь задавалися питанням, як при запиті отримати одразу всю потрібну структуру даних — від зв'язків ForeignKey до хитрих ManyToMany, то ви потрапили за адресою.
Сьогодні ми розберемо, як працювати з пов'язаними об'єктами у Django ORM за допомогою DRF. Ви дізнаєтеся:
- Як серіалізувати пов'язані об'єкти
ForeignKey,OneToOne,ManyToManyі включати їх у відповідь. - Як використовувати
related_nameдля налаштування серіалізаторів. - Приклади серіалізації пов'язаних об'єктів через
ModelSerializer. - Як кастомізувати серіалізацію пов'язаних даних.
Трохи теорії: що таке пов'язані об'єкти?
У 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']
Що ми зробили?
- Додали поле
author, яке використовує нашAuthorSerializerдля вкладеної серіалізації. - Вказали, що це поле
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 для роботи з серіалізацією пов'язаних об'єктів! 🎉
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ