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