> For the complete documentation index, see [llms.txt](https://ilyachch.gitbook.io/django-rest-framework-russian-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ilyachch.gitbook.io/django-rest-framework-russian-documentation/overview/navigaciya-po-api/validators.md).

# Валидаторы

***

источник: - validators.py

***

## Валидаторы

> Валидаторы могут быть полезны для повторного использования логики проверки между различными типами полей.
>
> — [Django documentation](https://docs.djangoproject.com/en/stable/ref/validators/)

В большинстве случаев, когда вы имеете дело с проверкой в DRF, вы просто полагаетесь на стандартную проверку полей, либо пишете явные методы проверки в сериализаторе или классах полей.

Однако иногда вам захочется поместить логику валидации в компоненты многократного использования, чтобы ее можно было легко повторно использовать в кодовой базе. Этого можно достичь с помощью функций валидатора и классов валидатора.

### Валидация в DRF

Валидация в сериализаторах DRF обрабатывается немного иначе, чем валидация в классе Django `ModelForm`.

При использовании `ModelForm` валидация выполняется частично на форме, частично на экземпляре модели. При использовании DRF валидация выполняется полностью на классе сериализатора. Это выгодно по следующим причинам:

* Обеспечивается правильное разделение обязанностей, что делает поведение вашего кода более очевидным.
* Легко переключаться между использованием коротких классов `ModelSerializer` и явными классами `Serializer`. Любое поведение проверки, используемое для `ModelSerializer`, легко повторить.
* Распечатка `repr` экземпляра сериализатора покажет вам, какие именно правила проверки он применяет. Нет никакого дополнительного скрытого поведения проверки, вызываемого на экземпляре модели.

При использовании `ModelSerializer` все это обрабатывается автоматически. Если вы хотите перейти к использованию классов `Serializer`, то вам необходимо явно определить правила валидации.

**Пример**

В качестве примера того, как DRF использует явную валидацию, возьмем простой класс модели, в котором есть поле с ограничением уникальности.

```python
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`:

```python
class CustomerReportSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomerReportRecord
```

Если мы откроем оболочку Django с помощью `manage.py shell`, то теперь мы можем

```python
>>> 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`. Мы видим, что ограничение уникальности явно обеспечивается валидатором на поле сериализатора.

Из-за этого более явного стиля DRF включает несколько классов валидаторов, которые отсутствуют в основном Django. Эти классы подробно описаны ниже. Валидаторы DRF, как и их аналоги в Django, реализуют метод `__eq__`, позволяющий сравнивать экземпляры на равенство.

***

### UniqueValidator

Этот валидатор может быть использован для обеспечения ограничения `unique=True` на поля модели. Он принимает один обязательный аргумент и необязательный аргумент `messages`:

* `queryset` *обязательно* - Это набор запросов, в отношении которого должна быть обеспечена уникальность.
* `message` - Сообщение об ошибке, которое должно быть использовано при неудачной проверке.
* `lookup` - Поиск, используемый для нахождения существующего экземпляра с проверяемым значением. По умолчанию `'exact'`.

Этот валидатор должен применяться к *полям сериализатора*, например, так:

```python
from rest_framework.validators import UniqueValidator

slug = SlugField(
    max_length=100,
    validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)
```

### UniqueTogetherValidator

Этот валидатор можно использовать для наложения ограничений `unique_together` на экземпляры моделей. Он имеет два обязательных аргумента и один необязательный аргумент `messages`:

* `queryset` *обязательно* - Это набор запросов, по которому должна быть обеспечена уникальность.
* `fields` *обязательно* - Список или кортеж имен полей, которые должны составлять уникальный набор. Они должны существовать как поля в классе сериализатора.
* `message` - Сообщение об ошибке, которое должно быть использовано при неудачной проверке.

Валидатор должен применяться к *классам сериализатора*, например, так:

```python
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` - Сообщение об ошибке, которое должно быть использовано при неудачной проверке.

Валидатор должен применяться к *классам сериализатора*, например, так:

```python
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`, вы, вероятно, просто будете полагаться на значения по умолчанию, которые генерирует для вас DRF, но если вы используете `Serializer` или просто хотите более явного контроля, используйте один из стилей, продемонстрированных ниже.

**Использование с записываемым полем даты.**

Если вы хотите, чтобы поле даты было доступно для записи, единственное, что стоит отметить, это то, что вы должны убедиться, что оно всегда доступно во входных данных, либо задав аргумент `default`, либо установив `required=True`.

```python
published = serializers.DateTimeField(required=True)
```

**Использование с полем даты, доступным только для чтения.**

Если вы хотите, чтобы поле даты было видно, но не редактировалось пользователем, то установите `read_only=True` и дополнительно задайте аргумент `default=...`.

```python
published = serializers.DateTimeField(read_only=True, default=timezone.now)
```

**Использование со скрытым полем даты.**

Если вы хотите, чтобы поле даты было полностью скрыто от пользователя, используйте `HiddenField`. Этот тип поля не принимает пользовательский ввод, а вместо этого всегда возвращает значение по умолчанию в `validated_data` в сериализаторе.

```python
published = serializers.HiddenField(default=timezone.now)
```

***

**Примечание**: Классы `UniqueFor<Range>Validator` накладывают неявное ограничение на то, что поля, к которым они применяются, всегда рассматриваются как обязательные. Поля со значениями `default` являются исключением из этого правила, так как они всегда предоставляют значение, даже если оно опущено при вводе пользователем.

***

***

**Примечание:** `HiddenField()` не появляется в сериализаторе `partial=True` (при выполнении запроса `PATCH`). Это поведение может измениться в будущем, следите за обновлениями на [github discussion](https://github.com/encode/django-rest-framework/discussions/8259).

***

## Расширенные значения полей по умолчанию

Валидаторы, применяемые к нескольким полям в сериализаторе, иногда могут потребовать ввода поля, которое не должно предоставляться клиентом API, но которое *доступно* в качестве входных данных для валидатора. Для этих целей используйте `HiddenField`. Это поле будет присутствовать в `validated_data`, но *не* будет использоваться в выходном представлении сериализатора.

**Примечание:** Использование поля `read_only=True` исключается из записываемых полей, поэтому оно не будет использовать аргумент `default=...`. Смотрите [3.8 объявление](https://www.django-rest-framework.org/community/3.8-announcement/#altered-the-behaviour-of-read_only-plus-default-on-field).

DRF включает в себя несколько параметров по умолчанию, которые могут быть полезны в данном контексте.

**CurrentUserDefault**

Класс по умолчанию, который можно использовать для представления текущего пользователя. Чтобы использовать его, `request` должен быть предоставлен как часть контекстного словаря при инстанцировании сериализатора.

```python
owner = serializers.HiddenField(
    default=serializers.CurrentUserDefault()
)
```

**CreateOnlyDefault**

Класс по умолчанию, который можно использовать *только для установки аргумента по умолчанию во время операций создания*. При обновлении поле опускается.

Он принимает один аргумент, который является значением по умолчанию или вызываемым модулем, который должен использоваться при создании.

```python
created_at = serializers.DateTimeField(
    default=serializers.CreateOnlyDefault(timezone.now)
)
```

***

## Ограничения валидаторов

Есть несколько неоднозначных случаев, когда вам придется явно обрабатывать валидацию, а не полагаться на классы сериализаторов по умолчанию, которые генерирует `ModelSerializer`.

В таких случаях вы можете отключить автоматически сгенерированные валидаторы, указав пустой список для атрибута сериализатора `Meta.validators`.

### Необязательные поля

По умолчанию валидация `unique_together` принудительно требует, чтобы все поля были `required=True`. В некоторых случаях вы можете захотеть явно применить `required=False` к одному из полей, и тогда желаемое поведение валидации будет неоднозначным.

В этом случае, как правило, необходимо исключить валидатор из класса сериализатора, а всю логику проверки написать явно либо в методе `.validate()`, либо в представлении.

Например:

```python
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` и распечатать экземпляр сериализатора, чтобы вы могли проверить поля и валидаторы, которые он автоматически генерирует для вас.

```python
>>> serializer = MyComplexModelSerializer()
>>> print(serializer)
class MyComplexModelSerializer:
    my_fields = ...
```

Также имейте в виду, что в сложных случаях часто бывает лучше явно определить свои классы сериализатора, а не полагаться на поведение `ModelSerializer` по умолчанию. Это потребует немного больше кода, но гарантирует, что результирующее поведение будет более прозрачным.

***

## Написание пользовательских валидаторов

Вы можете использовать любой из существующих валидаторов Django или написать свой собственный валидатор.

### Валидаторы, основанные на функциях

Валидатором может быть любой вызываемый объект, который при неудаче вызывает ошибку `serializers.ValidationError`.

```python
def even_number(value):
    if value % 2 != 0:
        raise serializers.ValidationError('This field must be an even number.')
```

**Валидация на уровне поля**

Вы можете задать пользовательскую проверку на уровне полей, добавив методы `.validate_<имя_поля>` в подкласс `Serializer`. Это описано в [документации по сериализаторам](https://ilyachch.gitbook.io/django-rest-framework-russian-documentation/overview/navigaciya-po-api/pages/-LegWRmrYTxtd2fN1kwI#валидация-на-уровне-поля)

### На основе класса

Чтобы написать валидатор на основе класса, используйте метод `__call__`. Валидаторы на основе классов полезны тем, что позволяют параметризировать и повторно использовать поведение.

```python
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` в качестве дополнительного аргумента.

```python
class MultipleOf:
    requires_context = True

    def __call__(self, value, serializer_field):
        ...
```
