JavaRush /Курси /Модуль 3: Django /Валідація даних у серіалізаторах

Валідація даних у серіалізаторах

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

Коли мова йде про прийом даних від користувача, валідація — це твій найкращий друг. Уяви, що твій користувач вирішив відправити в поле "вік" текст замість числа. Якщо ти не перевіриш дані, твій додаток, швидше за все, впаде з помилкою «що за текст у числовому полі?!». Валідація запобігає таким ситуаціям, гарантуючи, що дані відповідають твоїм очікуванням.

У Django REST Framework валідація відбувається на рівні серіалізаторів. DRF надає вбудовані механізми для перевірки даних, а також можливість реалізації кастомної логіки.

Вбудовані механізми валідації

DRF включає широкий набір вбудованих валідаторів, які роблять життя розробника простішим. Давайте розглянемо кілька базових прикладів.

Припустимо, у нас є застосунок для блогу, і кожне повідомлення має поле "заголовок". Ми хочемо, щоб заголовок був від 5 до 100 символів. Для цього можна використовувати валідатори MinLengthValidator та MaxLengthValidator:

from rest_framework import serializers
from django.core.validators import MinLengthValidator, MaxLengthValidator

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(
        validators=[
            MinLengthValidator(5),
            MaxLengthValidator(100),
        ]
    )
    content = serializers.CharField()

Якщо користувач спробує надіслати занадто короткий або занадто довгий заголовок, ви отримаєте повідомлення про помилку. DRF подбає про те, щоб такі дані не пройшли далі.

Перевірка мінімального та максимального значень чисел

Тепер припустимо, що ви створюєте серіалізатор для управління замовленнями, де можна вказати кількість товару. Логічно обмежити це значення, щоб користувач, наприклад, не замовив мінус 10 одиниць. Тут нам допоможуть валідатори MinValueValidator та MaxValueValidator:

from django.core.validators import MinValueValidator, MaxValueValidator

class OrderSerializer(serializers.Serializer):
    product_id = serializers.IntegerField()
    quantity = serializers.IntegerField(
        validators=[
            MinValueValidator(1),
            MaxValueValidator(100),
        ]
    )

Якщо користувач спробує замовити 0 або 200 одиниць товару, серіалізатор викине помилку.

Кастомна валідація

Іноді вбудованих валідаторів недостатньо. Наприклад, ви хочете переконатися, що в полі "email" вказана тільки дійсна корпоративна адреса, яка закінчується на @example.com. Що ж, час засукати рукава та написати свій власний валідатор!

Приклад валідатора для перевірки email

from rest_framework import serializers

def validate_example_email(email):
    if not email.endswith("@example.com"):
        raise serializers.ValidationError("Email має бути корпоративним (@example.com)")
    return email

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField(validators=[validate_example_email])
    username = serializers.CharField(max_length=50)

Тепер, якщо хтось спробує вказати адресу типу hacker@gmail.com, наш серіалізатор кине помилку з дружнім повідомленням.

Валідація на рівні поля

Іноді простіше додати логіку валідації прямо в серіалізатор, у певне поле. DRF підтримує метод _validate_<ім'я_поля>. Наприклад:

class ProductSerializer(serializers.Serializer):
    price = serializers.DecimalField(max_digits=10, decimal_places=2)

    def validate_price(self, value):
        if value <= 0:
            raise serializers.ValidationError("Ціна має бути більше нуля.")
        return value

Цей метод викликається автоматично для поля price.

Загальна валідація

А що, якщо нам потрібно перевірити одразу кілька полів? Наприклад, у нас є дата початку та дата закінчення, і ми хочемо переконатися, що початок завжди раніше кінця. Тут нам допоможе метод validate().

class EventSerializer(serializers.Serializer):
    start_date = serializers.DateField()
    end_date = serializers.DateField()

    def validate(self, data):
        if data['start_date'] < data['end_date']:
            raise serializers.ValidationError("Дата початку не може бути пізніше дати закінчення.")
        return data

Цей метод викликається після всіх індивідуальних перевірок полів.

Використання валідаторів у ModelSerializer

Якщо ти працюєш з ModelSerializer, то валідація також може виконуватись на рівні моделі. Але це не означає, що серіалізатори стають непотрібними. Вони чудово підходять для перевірки даних на рівні API.

Наприклад, у нас є модель:

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)

Серіалізатор із кастомною валідацією:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ['name', 'price']

    def validate_price(self, value):
        if value <= 0:
            raise serializers.ValidationError("Ціна товару повинна бути позитивною.")
        return value

Приклади використання валідації

Давайте об'єднаємо все, що ми вивчили, в один приклад. Створимо серіалізатор для застосунку, що дозволяє керувати завданнями.

from rest_framework import serializers

class TaskSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    description = serializers.CharField(allow_blank=True)
    due_date = serializers.DateField()
    priority = serializers.IntegerField()

    def validate_due_date(self, value):
        from datetime import date
        if value < date.today():
            raise serializers.ValidationError("Дата завершення завдання не може бути в минулому.")
        return value

    def validate_priority(self, value):
        if value < 1 or value > 5:
            raise serializers.ValidationError("Пріоритет завдання має бути від 1 до 5.")
        return value

    def validate(self, data):
        if 'urgent' in data['title'].lower() and data['priority'] < 3:
            raise serializers.ValidationError("Термінові завдання мають мати високий пріоритет (3 або вище).")
        return data

Тепер створимо завдання за допомогою цього серіалізатора:

data = {
    'title': "Urgent: Fix Production Bug",
    'description': "Critical issue in production",
    'due_date': "2023-09-15",
    'priority': 2
}

serializer = TaskSerializer(data=data)
if serializer.is_valid():
    print("Дані валідні:", serializer.validated_data)
else:
    print("Помилки валідації:", serializer.errors)

Цей код поверне помилку, оскільки завдання з згадкою "Urgent" мають мати високий пріоритет.

Зворотній зв'язок і типові помилки

Багато розробників забувають про валідацію на рівні серіалізатора, покладаючись на валідацію на рівні моделі. Хоча модель дійсно може багато чого, перевірка даних у серіалізаторі допомагає одразу надати користувачу зручні повідомлення про помилки.

Ще одна поширена помилка — плутанина між методами validate_<поле> і validate(). Пам'ятайте, що перший метод застосовується до конкретного поля, а другий — до всього об'єкта даних.

І не забудьте про тестування! Навіть найскладніший валідатор може дати збій, якщо його не протестувати. Використовуйте тестові дані для перевірки всіх крайніх випадків.

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