На чем делаешь валидацию входных данных в DRF
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Валидация входных данных в Django Rest Framework
DRF предоставляет мощный встроенный механизм валидации данных. Валидация — это критически важный процесс для обеспечения целостности и безопасности приложения.
1. Валидация на уровне полей (Field-level validation)
Это первый уровень валидации, который срабатывает автоматически для каждого поля в сериализаторе.
from rest_framework import serializers
class UserSerializer(serializers.Serializer):
username = serializers.CharField(
max_length=150,
min_length=3,
required=True
)
email = serializers.EmailField()
age = serializers.IntegerField(
min_value=0,
max_value=150
)
phone = serializers.CharField(
max_length=20,
allow_blank=False
)
DRF автоматически проверит формат email, диапазоны для целых чисел и строковые ограничения.
2. Методы-валидаторы на уровне полей (validate_field_name)
Для более сложной логики валидации одного поля можно использовать методы validate_<field_name>.
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=150)
email = serializers.EmailField()
age = serializers.IntegerField()
def validate_username(self, value):
"""Проверяем, что username не содержит недопустимые символы"""
if not value.isalnum():
raise serializers.ValidationError(
"Username может содержать только буквы и цифры"
)
return value
def validate_age(self, value):
"""Проверяем возраст"""
if value < 18:
raise serializers.ValidationError(
"Пользователь должен быть старше 18 лет"
)
return value
3. Валидация на уровне объекта (validate method)
Для проверки корреляции между несколькими полями используем метод validate.
class PasswordChangeSerializer(serializers.Serializer):
old_password = serializers.CharField(write_only=True)
new_password = serializers.CharField(write_only=True)
new_password_confirm = serializers.CharField(write_only=True)
def validate(self, data):
"""Проверяем согласованность паролей"""
if data["new_password"] != data["new_password_confirm"]:
raise serializers.ValidationError(
{"new_password_confirm": "Пароли не совпадают"}
)
if data["new_password"] == data["old_password"]:
raise serializers.ValidationError(
{"new_password": "Новый пароль совпадает со старым"}
)
return data
4. Кастомные валидаторы (validators)
Для переиспользуемой логики валидации создают отдельные функции-валидаторы.
def validate_even_number(value):
"""Проверяет, что число четное"""
if value % 2 != 0:
raise serializers.ValidationError(
"Это число не четное"
)
def validate_no_profanity(value):
"""Проверяет отсутствие запрещенных слов"""
forbidden_words = ["bad", "ugly"]
if any(word in value.lower() for word in forbidden_words):
raise serializers.ValidationError(
"Текст содержит недопустимые слова"
)
class ProductSerializer(serializers.Serializer):
name = serializers.CharField(validators=[validate_no_profanity])
quantity = serializers.IntegerField(validators=[validate_even_number])
5. Валидация на уровне ModelSerializer
При использовании ModelSerializer валидация тесно интегрирована с моделью Django.
from django.contrib.auth.models import User
class UserModelSerializer(serializers.ModelSerializer):
password_confirm = serializers.CharField(
write_only=True,
required=True
)
class Meta:
model = User
fields = ["username", "email", "password", "password_confirm"]
extra_kwargs = {
"password": {"write_only": True, "min_length": 8}
}
def validate(self, data):
if data["password"] != data.pop("password_confirm"):
raise serializers.ValidationError(
{"password_confirm": "Пароли не совпадают"}
)
return data
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
6. Валидация в представлении (View-level validation)
Иногда нужна валидация, которая зависит от контекста запроса (например, проверка прав доступа).
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class CreateOrderView(APIView):
def post(self, request):
serializer = OrderSerializer(data=request.data)
# Сначала базовая валидация
if not serializer.is_valid():
return Response(
serializer.errors,
status=status.HTTP_400_BAD_REQUEST
)
# Затем проверка прав и бизнес-логики
if not request.user.is_authenticated:
return Response(
{"detail": "Требуется аутентификация"},
status=status.HTTP_401_UNAUTHORIZED
)
if not request.user.has_balance():
return Response(
{"detail": "Недостаточно средств"},
status=status.HTTP_400_BAD_REQUEST
)
order = serializer.save(user=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
7. Встроенные валидаторы DRF
DRF предоставляет готовые валидаторы для часто встречающихся случаев:
from rest_framework.validators import (
UniqueValidator,
UniqueTogetherValidator
)
class ArticleSerializer(serializers.ModelSerializer):
slug = serializers.SlugField(
validators=[UniqueValidator(queryset=Article.objects.all())]
)
class Meta:
model = Article
fields = ["title", "slug", "author", "published_date"]
validators = [
UniqueTogetherValidator(
queryset=Article.objects.all(),
fields=["author", "published_date"],
message="Автор не может опубликовать две статьи в один день"
)
]
Лучшие практики валидации
- Ранняя валидация — проверяйте данные как можно раньше в процессе обработки
- Понятные сообщения об ошибках — помогите пользователю понять, что не так
- Разделение ответственности — не смешивайте валидацию с бизнес-логикой
- Переиспользование валидаторов — создавайте кастомные валидаторы для повторяющейся логики
- Логирование ошибок валидации — отслеживайте попытки передачи невалидных данных
Валидация в DRF — это мощный инструмент, который при правильном использовании значительно упрощает разработку и повышает качество приложения.