Порядок проверки Serializer в Django REST Framework
Ситуация
Во время работы с проверкой в Django REST Framework ModelSerializer
я заметил, что поля Meta.model
всегда проверяются, даже если это не имеет смысла. Возьмем следующий пример для сериализации модели User
:
- У меня есть конечная точка, которая создает пользователя. Таким образом, поле
password
и поле confirm_password
. Если два поля не совпадают, пользователь не может быть создан. Аналогично, если запрошенный username
уже существует, пользователь не может быть создан.
- Пользовательские POST-коды не соответствуют значениям для каждого из указанных выше полей.
- В сериализаторе была реализована реализация
validate
(см. ниже), улавливая не совпадающие поля password
и confirm_password
Реализация validate
:
def validate(self, data):
if data['password'] != data.pop('confirm_password'):
raise serializers.ValidationError("Passwords do not match")
return data
Проблема
Даже когда ValidationError
поднят на validate
, ModelSerializer
по-прежнему запрашивает базу данных, чтобы проверить, используется ли username
. Это видно в списке ошибок, который возвращается из конечной точки; присутствуют как модельные, так и непарные ошибки.
Следовательно, я хотел бы знать, как предотвратить проверку модели до тех пор, пока не завершится проверка не-поля, сохранив вызов в моей базе данных.
Попытка решения
Я пытаюсь пройти через источник DRF, чтобы выяснить, где это происходит, но мне не удалось найти то, что мне нужно переопределить, чтобы заставить это работать.
Ответы
Ответ 1
Поскольку, скорее всего, в поле username
установлено значение unique=True
, Django REST Framework автоматически добавляет валидатор, который проверяет, что новое имя пользователя уникально. Фактически вы можете подтвердить это, выполнив repr(serializer())
, который покажет вам все автоматически сгенерированные поля, которые включают в себя валидаторы.
Проверка выполняется в определенном, недокументированном порядке
Таким образом, проблема, которую вы видите, заключается в том, что валидация на уровне поля вызывается перед проверкой уровня на уровне сериализации. Хотя я бы не рекомендовал его, вы можете удалить валидатор на уровне поля, установив extra_kwargs
в ваш metalalizer meta.
class Meta:
extra_kwargs = {
"username": {
"validators": [],
},
}
Вам нужно будет повторно выполнить проверку unique
в вашей собственной проверке, хотя вместе с любыми дополнительными валидаторами, которые были автоматически сгенерированы.
Ответ 2
Я не верю, что вышеупомянутые решения уже работают. В моем случае у моей модели есть поля "first_name" и "last_name", но API получит только "имя".
Настройка "extra_kwargs" и "валидаторы" в мета-классе, кажется, не имеет никакого эффекта, имя first_name и last_name всегда считаются обязательными, и валидаторы всегда вызываются. Я не могу перегрузить поля символов first_name/last_name с помощью
anotherrepfor_first_name = serializers.CharField(source=first_name, required=False)
поскольку имена имеют смысл. После многих часов разочарования я нашел единственный способ переопределить валидаторы с экземпляром ModelSerializer, чтобы переопределить инициализатор класса следующим образом (простить неправильный отступ):
class ContactSerializer(serializers.ModelSerializer):
name = serializers.CharField(required=True)
class Meta:
model = Contact
fields = [ 'name', 'first_name', 'last_name', 'email', 'phone', 'question' ]
def __init__(self, *args, **kwargs):
self.fields['first_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True)
self.fields['last_name'] = serializers.CharField(required=False, allow_null=True, allow_blank=True)
return super(ContactSerializer, self).__init__(*args, **kwargs)
def create(self, validated_data):
return Contact.objects.create()
def validate(self, data):
"""
Remove name after getting first_name, last_name
"""
missing = []
for k in ['name', 'email', 'question']:
if k not in self.fields:
missing.append(k)
if len(missing):
raise serializers.ValidationError("Ooops! The following fields are required: %s" % ','.join(missing))
from nameparser import HumanName
names = HumanName(data['name'])
names.capitalize()
data['last_name'] = names.last
if re.search(r'\w+', names.middle):
data['first_name'] = ' '.join([names.first, names.middle])
else:
data['first_name'] = names.first
del(data['name'])
return data
Теперь в документе говорится, что разрешение пустым и нулевым символьным полям - нет, но это сериализатор, а не модель, и, поскольку API вызывается всеми видами ковбоев, мне нужно покрыть мои базы.