Подтвердить что ты не робот

Дополнительные поля Serializer в Django REST Framework 3

Ситуация

Я создаю простую конечную точку, которая позволяет создавать пользователя. Мне нужно поле, которое отсутствует в моей пользовательской модели (т.е. confirm_password). Я проведу проверку, которая сравнивает это поле и другое поле, которое находится в моей модели, и никогда больше не используйте дополнительное поле в сериализаторе.

Проблема

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

Попытка решения

Я создал UserSerializer, который выглядит так:

from django.contrib.auth import get_user_model
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    confirm_password = serializers.CharField(allow_blank=False)

    def validate(self, data):
        """
        Checks to be sure that the received password and confirm_password
        fields are exactly the same
        """
        if data['password'] != data.pop('confirm_password'):
            raise serializers.ValidationError("Passwords do not match")
        return data

    def create(self, validated_data):
        """
        Creates the user if validation succeeds
        """
        password = validated_data.pop('password', None)
        user = self.Meta.model(**validated_data)
        user.set_password(password)
        user.save()
        return user

    class Meta:
        # returns the proper auth model
        model = get_user_model()
        # fields that will be deserialized
        fields = ['password', 'confirm_password',
                  'username', 'first_name', 'last_name', 'email']
        # fields that will be serialized only
        read_only_fields = ['is_staff', 'is_superuser']
        # fields that will be deserialized only
        write_only_fields = ['password' 'confirm_password']

Я надеялся, что popping confirm_password в validate позаботится о моих проблемах, но я просто получаю следующее:

Получите KeyError при попытке получить значение для поля confirm_password в serializer UserSerializer. Поле сериализатора может быть названо неправильно и не соответствует никакому атрибуту или ключу в экземпляре OrderedDict

4b9b3361

Ответ 1

Вы ищете поле только для записи, так как я предполагаю, что вы не захотите отображать подтверждение пароля в API. Django REST Framework ввел параметр write_only в шкалу времени 2.3.x в дополнение к параметру read_only, поэтому выполняется только проверка времени, когда выполняется обновление. Мета-свойство write_only_fields было добавлено примерно в одно и то же время, но важно понять, как обе эти функции работают вместе.

Свойство meta write_only_fields автоматически добавит свойство write_only к полю, когда оно будет автоматически создано, например, для поля password в модели User. Это не будет сделано для любых полей, которые не относятся к модели, или полей, которые явно указаны в сериализаторе. В вашем случае вы явно указываете поле confirm_password вашего сериализатора, поэтому он не работает.

Получите KeyError при попытке получить значение для поля confirm_password в serializer UserSerializer. Поле сериализатора может быть названо неправильно и не соответствует атрибуту или ключу в экземпляре OrderedDict

Это возникает во время повторной сериализации созданного пользователя, когда он пытается сериализовать поле confirm_password. Поскольку он не может найти поле в модели User, он вызывает эту ошибку, которая пытается объяснить проблему. К сожалению, потому что это у нового пользователя, он говорит вам смутно взглянуть на экземпляр OrderedDict вместо экземпляра User.

class UserSerializer(serializers.ModelSerializer):
    confirm_password = serializers.CharField(allow_blank=False, write_only=True)

Если вы явно указываете write_only в поле сериализатора и удаляете поле из write_only_fields, тогда вы должны увидеть поведение, которое вы ожидаете.

Вы можете найти документацию об этом на этой ссылке

Ответ 2

Также полезно для реализации вложенных сериализаторов, представляющих модели, когда корневая модель не имеет прямого доступа к полям, которые вы хотите использовать. Спасибо @vyscond;) Fyi вот мой случай:

models.py

class Company(models.Model):
    permission_classes = (
        IsCompanyMember,
    )

    name = models.CharField(
        unique=True,
        max_length=100,
        verbose_name='company name',
        null=False
    )
class Profile(models.Model):

    company = models.ForeignKey(Company, on_delete=models.CASCADE, null=True)
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    is_company_admin = models.BooleanField(default=False, null=False)
    is_email_validated = models.BooleanField(default=False, null=False)
    is_approved_by_company_admin = models.BooleanField(default=False, null=False)

serializer.py

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ('name',)

class CustomUserRegistrationSerializer(serializers.ModelSerializer):
    password = serializers.CharField(style={'input_type': 'password'},
                                 write_only=True,
                                 validators=settings.get('PASSWORD_VALIDATORS'))

    company = CompanySerializer(allow_null=False, required=True,write_only=True)

    class Meta:
        model = User
        fields = ('username',
                  'email',
                  'password',
                  'company',
                  'first_name',
                  'last_name')