Когда речь заходит о приёме данных от пользователя, валидация — это ваш лучший друг. Представьте, что ваш пользователь решил отправить в поле "возраст" текст вместо числа. Если вы не проверите данные, ваше приложение, скорее всего, упадёт с ошибкой «что за текст в числовом поле?!». Валидация предотвращает такие ситуации, гарантируя, что данные соответствуют вашим ожиданиям.
В 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(). Помните, что первый метод применяется к конкретному полю, а второй — ко всему объекту данных.
И не забудьте про тестирование! Даже самый сложный валидатор может дать сбой, если его не протестировать. Используйте тестовые данные для проверки всех крайних случаев.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ