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(). Помните, что первый метод применяется к конкретному полю, а второй — ко всему объекту данных.

И не забудьте про тестирование! Даже самый сложный валидатор может дать сбой, если его не протестировать. Используйте тестовые данные для проверки всех крайних случаев.

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