JavaRush /Курси /Модуль 3: Django /Використання методу `to_representation()`

Використання методу `to_representation()`

Модуль 3: Django
Рівень 17 , Лекція 8
Відкрита

У процесі роботи з API іноді потрібно змінювати формат даних перед їх відправленням клієнту. Наприклад, ви хочете:

  • Спрощено відображати пов'язані об'єкти.
  • Перейменувати поля або об'єднати дані з кількох полів.
  • Виключити або змінити деякі дані залежно від умов.

Метод to_representation() — той самий магічний інструмент, який дозволяє кастомізувати процес перетворення даних об'єктів моделі в JSON та інші формати.

Основи методу to_representation()

Метод to_representation() відповідає за перетворення об'єкта моделі у серіалізовані дані. Уявіть, що він як добра бабуся, яка перекладає складну мову ваших моделей у зручний для клієнта JSON:

class MyCustomSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ['id', 'name', 'created_at']

    def to_representation(self, instance):
        # Перетворюємо об'єкт моделі у зрозумілий формат
        representation = super().to_representation(instance)

        # Додаємо кастомне поле
        representation['formatted_date'] = instance.created_at.strftime('%d-%m-%Y')
        return representation

Тут, після стандартного перетворення даних за допомогою базового to_representation, ми додаємо нове поле formatted_date, яке містить відформатовану дату.

Коли потрібно перевизначати to_representation()?

1. Змінюємо структуру даних

Припустимо, є модель з одним ключовим зв'язаним полем:

class Book(models.Model):
    title = models.CharField(max_length=255)
    published_date = models.DateField()
    author = models.ForeignKey("Author", on_delete=models.CASCADE)

class Author(models.Model):
    name = models.CharField(max_length=255)
    country = models.CharField(max_length=100)

Стандартний ModelSerializer легко серіалізує Book, включаючи author, але він поверне лише id автора:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ["title", "published_date", "author"]

Результат:

{
  "title": "Python for Humans",
  "published_date": "2023-09-21",
  "author": 1  # ID автора
}

Ваш клієнт не хоче бачити ID автора, він хоче бачити ім'я. Використовуємо to_representation:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['title', 'published_date', 'author']

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        # Замінюємо ID автора на його ім'я
        representation['author'] = instance.author.name
        return representation

Результат:

{
  "title": "Python for Humans",
  "published_date": "2023-09-21",
  "author": "Guido van Rossum"
}

Тепер читабельність покращилась. Клієнт задоволений!

2. Умовне відображення даних

Іноді важливо адаптувати дані залежно від умов. Наприклад, поле author_country доступне лише для адмінів:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['title', 'published_date', 'author']

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        request = self.context.get('request')

        # Додаємо країну, якщо користувач - адмін
        if request and request.user.is_staff:
            representation['author_country'] = instance.author.country
        return representation

3. Робота з вкладеними структурами

Для вкладених даних to_representation() дозволяє більш гнучко контролювати, як зв'язані об'єкти будуть відображатися. Наприклад, якщо ви хочете показати більше даних про автора:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['title', 'published_date', 'author']

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['author'] = {
            'name': instance.author.name,
            'country': instance.author.country
        }
        return representation

Результат:

{
  "title": "Python for Humans",
  "published_date": "2023-09-21",
  "author": {
    "name": "Guido van Rossum",
    "country": "Netherlands"
  }
}

Перевизначення to_representation(): Best Practices

  1. Використовуйте super().to_representation()
    Починайте з отримання стандартної серіалізованої версії об'єкта. Це позбавляє необхідності вручну описувати кожне поле.

  2. Мінімізуйте логіку в to_representation()
    Робіть метод простим і зрозумілим. Якщо потрібна складна обробка, краще винесіть її в окремий метод.

  3. Контекст має значення
    Використовуйте self.context, щоб адаптувати дані залежно від користувача або запиту.

  4. Уникайте перевантаження даних
    Якщо один об'єкт має багато вкладених даних, подумайте про пагінацію або фільтрацію замість того, щоб запихати у відповідь все й одразу.

Помилки, яких слід уникати

Перша і найчастіша помилка — ігнорувати базовий метод super().to_representation(). Якщо цього не зробити, всі стандартні поля, описані в Meta: fields, зникнуть, і потрібно буде вручну їх серіалізувати.

Крім того, завжди потрібно пам’ятати про продуктивність. Занадто складна логіка в to_representation() збільшує час обробки запиту, особливо якщо ви виконуєте запити до бази даних всередині методу. Наприклад, уникайте такого:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['title', 'published_date']

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        # Погана практика — кожен виклик робить новий запит до бази!
        representation['author'] = {
            'name': Author.objects.get(id=instance.author_id).name
        }
        return representation

Використовуйте select_related або заздалегідь завантажуйте необхідні дані.

Приклад: фінальна версія

Реалізуємо серіалізацію, яка:

  1. Показує базову інформацію про книгу.
  2. Включає детальну інформацію про автора.
  3. Умовно додає інформацію про країну, якщо запит від адміністратора.
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['title', 'published_date', 'author']

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['author'] = {
            'name': instance.author.name,
            'country': instance.author.country if self.context.get('request').user.is_staff else None
        }
        return representation

Результат:

  • Для звичайного користувача:
    {
      "title": "Python for Humans",
      "published_date": "2023-09-21",
      "author": {
        "name": "Guido van Rossum",
        "country": null
      }
    }
    
  • Для адміністратора:
    {
    "title": "Python for Humans",
    "published_date": "2023-09-21",
    "author": {
      "name": "Guido van Rossum",
      "country": "Netherlands"
    }
    }
    

Метод to_representation() стане вашим найкращим другом, коли потрібно адаптувати дані для клієнта (і зробити їх щасливими!). Головне — не забувайте про продуктивність і дбайливе ставлення до даних.

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