【问题标题】:Additional Serializer Fields in Django REST Framework 3Django REST Framework 3 中的附加序列化器字段
【发布时间】:2015-02-19 20:57:26
【问题描述】:

情况

我正在创建一个允许创建用户的简单端点。我需要一个不在我的用户模型中的字段(即confirm_password)。我将运行验证,比较这个字段和我模型中的另一个字段,然后在序列化程序中不再使用附加字段。

问题

DRF 版本 3 改变了实现这一点的过程,我不太明白文档建议我做什么。有关文档,请参阅 here

尝试解决方案

我创建了一个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']

我希望在validate 中弹出confirm_password 可以解决我的问题,但我得到以下信息:

在序列化程序 UserSerializer 上尝试获取字段 confirm_password 的值时得到 KeyError。序列化器字段可能命名不正确,并且与 OrderedDict 实例上的任何属性或键都不匹配

【问题讨论】:

  • 提醒一下,对于大多数人来说,将Meta 类放在序列化程序的顶部是很常见的,因为它包含您通常要查找的大部分信息。
  • 啊,这很有道理。我很感激!

标签: python django django-rest-framework


【解决方案1】:

您正在寻找一个只写字段,因为我假设您不想在 API 中显示密码确认。 Django REST Framework 在 2.3.x 时间轴中引入了 write_only 参数来补充 read_only 参数,因此唯一运行验证的时间是在进行更新时。 write_only_fields 元属性大约是在同一时间添加的,但了解这两者如何协同工作很重要。

write_only_fields 元属性会在自动创建时自动将write_only 属性添加到字段中,例如User 模型上的password 字段。它不会对不在模型上的任何字段或已在序列化程序上明确指定的字段执行此操作。在您的情况下,您在序列化程序上明确指定 confirm_password 字段,这就是它不起作用的原因。

在尝试获取序列化程序 UserSerializer 上字段 confirm_password 的值时得到 KeyError。序列化器字段可能命名不正确,并且与 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 中删除该字段,那么您应该会看到您所期望的行为。

您可以在 link 上找到有关此的文档

【讨论】:

    【解决方案2】:

    当根模型无法直接访问您要使用的字段时,对于表示模型的嵌套序列化器实现也很有用。 谢谢@vyscond ;) 仅供参考,这是我的情况:

    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')
    

    【讨论】:

      猜你喜欢
      • 2020-06-27
      • 2015-12-19
      • 1970-01-01
      • 2013-07-07
      • 1970-01-01
      • 2020-06-09
      • 2014-05-05
      • 2018-06-07
      • 2015-07-01
      相关资源
      最近更新 更多