Валидаторы
Валидаторы могут быть полезны для повторного использования логики валидации между различными типами полей.
Большую часть времени, работая с валидацией в REST фреймворке, вы будете полагаться на стандартную валидацию полей или писать явные методы валидации в классах сериализаторов или полей.
Однако, иногда вам захочется поместить логику валидации в переиспользуемые компоненты, чтобы её легко можно было использовать по всему коду. Это можно сделать, используя функции-валидаторы и классы-валидаторы.
Валидация в REST фреймворке
Валидация в сериализаторах Django REST фреймворка обрабатывается немного по-другому, чем в Django's ModelForm классе.
С ModelForm валидация выполняется частично на форме, и частично на экземпляре модели. С REST фреймворком валидация выполняется полностью на классе сериализатора. Это выгодно по следующим причинам:
- Это вводит надлежащее разделение задач, делая поведение вашего кода более понятным.
- Легко переключаться между использованием сокращённых
ModelSerializerклассов и использованием явныхSerializerклассов. Любое поведение валидации, используемое дляModelSerializerлегко дублируется. - Вывод
reprэкземпляра сериализатора покажет вам точно, какие правила валидации он применяет. Нет дополнительного скрытого поведения валидации, вызываемого на экземпляре модели.
Когда вы используете ModelSerializer всё это обрабатывается автоматически. Если вы хотите перейти к использованию Serializer классов вместо этого, вам нужно явно определить правила валидации.
Пример
В качестве примера использования явной валидации в REST фреймворке, мы возьмём простой класс модели, у которого есть поле с ограничением уникальности.
class CustomerReportRecord(models.Model):
time_raised = models.DateTimeField(default=timezone.now, editable=False)
reference = models.CharField(unique=True, max_length=20)
description = models.TextField()
Вот базовый ModelSerializer , который мы можем использовать для создания или обновления экземпляров CustomerReportRecord:
class CustomerReportSerializer(serializers.ModelSerializer):
class Meta:
model = CustomerReportRecord
Если мы откроем оболочку Django с помощью manage.py shell мы теперь можем
>>> from project.example.serializers import CustomerReportSerializer
>>> serializer = CustomerReportSerializer()
>>> print(repr(serializer))
CustomerReportSerializer():
id = IntegerField(label='ID', read_only=True)
time_raised = DateTimeField(read_only=True)
reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>])
description = CharField(style={'type': 'textarea'})
Интересной частью здесь является поле reference. Мы видим, что ограничение уникальности явно накладывается валидатором на поле сериализатора.
Из-за этого более явного стиля REST фреймворк включает несколько классов валидаторов, которые недоступны в базовом Django. Эти классы описаны ниже. Валидаторы REST фреймворка, как и их аналоги из Django, реализуют метод __eq__ , позволяя сравнивать экземпляры на равенство.
UniqueValidator
Этот валидатор может использоваться для наложения ограничения unique=True на поля модели. Он принимает один обязательный аргумент и необязательный messages аргумент:
-
querysetобязательный - Это набор запросов, относительно которого должна быть проверена уникальность. -
message- Сообщение об ошибке, которое должно использоваться при ошибке валидации. -
lookup- Использование поиска для нахождения существующего экземпляра со значением, которое валидируется. По умолчанию'exact'.
Этот валидатор должен применяться к полям сериализатора, как показано ниже:
from rest_framework.validators import UniqueValidator
slug = SlugField(
max_length=100,
validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)
UniqueTogetherValidator
Этот валидатор может быть использован для наложения ограничений unique_together на экземпляры модели. Он имеет два обязательных аргумента и один необязательный messages аргумент:
-
querysetобязательный - Это набор запросов, относительно которого должна быть проверена уникальность. -
fieldsобязательный - Список или кортеж имён полей, которые должны составлять уникальный набор. Они должны существовать в качестве полей в классе сериализатора. -
message- Сообщение об ошибке, которое должно использоваться при ошибке валидации.
Валидатор должен применяться к классам сериализаторов, как показано ниже:
from rest_framework.validators import UniqueTogetherValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
# ToDo items belong to a parent list, and have an ordering defined
# by the 'position' field. No two items in a given list may share
# the same position.
validators = [
UniqueTogetherValidator(
queryset=ToDoItem.objects.all(),
fields=['list', 'position']
)
]
Примечание: Класс UniqueTogetherValidator всегда накладывает неявное ограничение, что все поля, к которым он применяется, всегда рассматриваются как обязательные. Поля со значениями default являются исключением из этого правила, так как они всегда предоставляют значение даже при отсутствии в пользовательском вводе.
UniqueForDateValidator
UniqueForMonthValidator
UniqueForYearValidator
Эти валидаторы могут быть использованы для наложения ограничений unique_for_date, unique_for_month и unique_for_year на экземпляры модели. Они принимают следующие аргументы:
-
querysetобязательный - Это набор запросов, относительно которого должна быть проверена уникальность. -
fieldобязательный - Имя поля, относительно которого будет проверена уникальность в заданном диапазоне дат. Оно должно существовать в качестве поля в классе сериализатора. -
date_fieldобязательный - Имя поля, которое будет использоваться для определения диапазона дат для ограничения уникальности. Оно должно существовать в качестве поля в классе сериализатора. -
message- Сообщение об ошибке, которое должно использоваться при ошибке валидации.
Валидатор должен применяться к классам сериализаторов, как показано ниже:
from rest_framework.validators import UniqueForYearValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
# Blog posts should have a slug that is unique for the current year.
validators = [
UniqueForYearValidator(
queryset=BlogPostItem.objects.all(),
field='slug',
date_field='published'
)
]
Поле даты, используемое для валидации, всегда должно быть присутствовать в классе сериализатора. Вы не можете просто положиться на класс модели default=..., потому что значение, используемое по умолчанию, не будет сгенерировано до выполнения валидации.
Есть несколько стилей, которые вы можете использовать в зависимости от того, как вы хотите, чтобы ваш API себя вёл. Если вы используете ModelSerializer, вы, вероятно, просто будете полагаться на значения по умолчанию, которые сгенерирует для вас REST фреймворк, но если вы используете Serializer или просто хотите больше контроля, используйте один из стилей, продемонстрированных ниже.
Использование с изменяемым полем даты.
Если вы хотите, чтобы поле даты было изменяемым, единственное, что стоит отметить, это то, что оно должно всегда быть доступно в входных данных, либо установив аргумент default, либо установив required=True.
published = serializers.DateTimeField(required=True)
Использование с только-для-чтения полем даты.
Если вы хотите, чтобы поле даты было видимым, но не редактируемым пользователем, установите read_only=True и дополнительно установите аргумент default=....
published = serializers.DateTimeField(read_only=True, default=timezone.now)
Использование с скрытым полем даты.
Если вы хотите, чтобы поле даты было полностью скрыто от пользователя, используйте HiddenField. Этот тип поля не принимает пользовательский ввод, а вместо этого всегда возвращает своё значение по умолчанию в validated_data сериализатора.
published = serializers.HiddenField(default=timezone.now)
Примечание: Классы UniqueFor<Range>Validator накладывают неявное ограничение, что поля, к которым они применяются, всегда рассматриваются как обязательные. Поля со значениями default являются исключением из этого правила, так как они всегда предоставляют значение даже при отсутствии в пользовательском вводе.
Примечание: HiddenField() не появляется в partial=True сериализаторе (при выполнении PATCH запроса). Это поведение может измениться в будущем, следите за обновлениями на github discussion.
Расширенные значения по умолчанию для полей
Валидаторы, применяемые к нескольким полям в сериализаторе, иногда могут потребовать входное поле, которое не должно предоставляться клиентом API, но которое доступно как входное значение для валидатора. Для этих целей используйте HiddenField . Это поле будет присутствовать в validated_data , но не будет использоваться в представлении вывода сериализатора.
Примечание: Использование поля read_only=True исключено из изменяемых полей, поэтому оно не будет использовать аргумент default=…. См. сообщение о выпуске 3.8.
REST фреймворк включает несколько значений по умолчанию, которые могут быть полезны в этом контексте.
CurrentUserDefault
Класс по умолчанию, который можно использовать для представления текущего пользователя. Для использования этого класса, 'request' должен быть предоставлен в словаре контекста при создании сериализатора.
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
CreateOnlyDefault
Класс по умолчанию, который можно использовать, чтобы установить значение по умолчанию только во время операций создания. При обновлениях поле опущено.
Он принимает один аргумент, который является значением по умолчанию или вызываемой функцией, которая должна использоваться во время операций создания.
created_at = serializers.DateTimeField(
default=serializers.CreateOnlyDefault(timezone.now)
)
Ограничения валидаторов
Есть некоторые неоднозначные случаи, когда вам нужно вместо этого явно обработать валидацию, а не полагаться на стандартные классы сериализаторов, которые ModelSerializer генерирует.
В этих случаях вы можете отключить автоматически сгенерированные валидаторы, указав пустой список для атрибута сериализатора Meta.validators.
Необязательные поля
По умолчанию валидация "unique together" требует, чтобы все поля были required=True. В некоторых случаях вы можете явно применить required=False к одному из полей, в этом случае желаемое поведение валидации является неоднозначным.
В этом случае вам, как правило, потребуется исключить валидатор из класса сериализатора и вместо этого написать логику валидации явно, либо в методе .validate() , либо в представлении.
Например:
class BillingRecordSerializer(serializers.ModelSerializer):
def validate(self, attrs):
# Apply custom validation either here, or in the view.
class Meta:
fields = ['client', 'date', 'amount']
extra_kwargs = {'client': {'required': False}}
validators = [] # Remove a default "unique together" constraint.
Обновление вложенных сериализаторов
При применении обновления к существующему экземпляру валидаторы уникальности будут исключать текущий экземпляр из проверки уникальности. Текущий экземпляр доступен в контексте проверки уникальности, потому что он существует как атрибут в сериализаторе, изначально переданный с помощью instance=... при создании сериализатора.
В случае операций обновления вложенных сериализаторов нет способа применить это исключение, потому что экземпляр недоступен.
Опять же, вы, вероятно, захотите явно удалить валидатор из класса сериализатора и написать код для ограничения валидации явно, в методе .validate() или в представлении.
Отладка сложных случаев
Если вы не уверены, какое поведение сгенерирует класс ModelSerializer , обычно лучше выполнить manage.py shell, и распечатать экземпляр сериализатора, чтобы вы могли проверить поля и валидаторы, которые он автоматически сгенерирует для вас.
>>> serializer = MyComplexModelSerializer()
>>> print(serializer)
class MyComplexModelSerializer:
my_fields = ...
Также имейте в виду, что в сложных случаях часто лучше явно определить свои классы сериализаторов, а не полагаться на поведение ModelSerializer по умолчанию. Это требует немного больше кода, но гарантирует, что полученное поведение будет более прозрачным.
Написание пользовательских валидаторов
Вы можете использовать любые существующие валидаторы Django или написать свои собственные пользовательские валидаторы.
Основанные на функциях
Валидатор может быть любой вызываемой функцией, которая вызывает исключение serializers.ValidationError при ошибке.
def even_number(value):
if value % 2 != 0:
raise serializers.ValidationError('This field must be an even number.')
Валидация на уровне поля
Вы можете указать пользовательскую валидацию на уровне поля, добавив методы .validate_<field_name> в свой подкласс Serializer. Это описано в документации по сериализаторам.
Основанные на классах
Чтобы написать валидатор на основе класса, используйте метод __call__. Классовые валидаторы полезны, так как позволяют параметризовать и повторно использовать поведение.
class MultipleOf:
def __init__(self, base):
self.base = base
def __call__(self, value):
if value % self.base != 0:
message = 'This field must be a multiple of %d.' % self.base
raise serializers.ValidationError(message)
Доступ к контексту
В некоторых сложных случаях вам может потребоваться, чтобы валидатор получал поле сериализатора, с которым он используется, в качестве дополнительного контекста. Вы можете сделать это, установив атрибут requires_context = True в классе валидатора. Метод __call__ затем будет вызван с serializer_field или serializer в качестве дополнительного аргумента.
class MultipleOf:
requires_context = True
def __call__(self, value, serializer_field):
...
Copyright © 2011–present Encode OSS Ltd.
Licensed under the BSD License.
https://www.django-rest-framework.org/api-guide/validators/