验证Django Rest Framework序列化器中唯一的电子邮件

4

我有一个模型,其中Email字段是唯一的。我认为它会使每个电子邮件都变成小写,因为User实例上有normalize_email()方法。然而,它只规范化域部分,因此如果company@gmail.com存在,那么Company@gmail.com被视为唯一。所以我决定在我的序列化器中创建validate_email(),始终返回小写电子邮件。这是一个字段验证,并在文档中进行了描述。

    def validate_email(self, value):
        return value.lower()

然而,看起来此方法返回序列化器检查数据库中是否存在该值后的值。以下是一个例子:
如果我尝试使用 user@gmail.com 创建用户并且它已经存在,它将返回“用户已存在”,这是可以预料的。但是,如果我用 User@gmail.com 运行它,它将首先运行一个 SQL 请求,并检查 User@gmail.com != user@gmail.com,然后它将尝试创建一个新实例,其中包含 user@gmail.com,因为它是从 validate_email() 返回的,但会引发 IntegrityError,因为它变成了已经在 DB 中的 user@gmail.com!
我可以做像这样的事情:
    def validate_email(self, value):
        norm_email = value.lower()
        if User.objects.filter(email=norm_email).exists():
            raise serializers.ValidationError("Not unique email")
        return norm_email

但这是对数据库的另一个请求,而我不想这样。

所以我的问题是,有哪种方法可以运行SQL请求来检查数据库中的唯一性?这样我就可以覆盖它并传递已经小写的值了。

1个回答

6

使用 iexact--(Django文档) 进行查询。

User.objects.filter(<b>email__iexact=norm_email</b>).exists()

更新

DRF会在查询时检查唯一约束条件,因为我们在模型字段中设置了unique=True。为了避免这种情况,我们需要在序列化器中明确定义email字段,这样可以绕过唯一性检查验证

class UserSerializer(serializers.ModelSerializer):
    <b>email = serializers.EmailField()

    def validate_email(self, value):
        lower_email = value.lower()
        if User.objects.filter(email__iexact=lower_email).exists():
            raise serializers.ValidationError("Duplicate")
        return lower_email</b>

    class Meta:
        model = User
        fields = ('email',)

Django shell输出

In [17]:  print(len(connection.queries))
7

In [18]: class UserSerializer(serializers.ModelSerializer):
    ...:     email = serializers.EmailField()
    ...: 
    ...:     def validate_email(self, value):
    ...:         lower_email = value.lower()
    ...:         if User.objects.filter(email__iexact=lower_email).exists():
    ...:             raise serializers.ValidationError("Duplicate")
    ...:         return lower_email
    ...: 
    ...:     class Meta:
    ...:         model = User
    ...:         fields = ('email',)
    ...: 

In [19]: print(len(connection.queries))
7

In [20]: s = UserSerializer(data={'email': 'Foo@gmail.com'})

In [21]: print(len(connection.queries))
7

In [22]: try:
    ...:     s.is_valid(True)
    ...: except serializers.ValidationError:
    ...:     print("raised validation error")
    ...: 
raised validation error

In [23]: print(len(connection.queries))
8

就像我说的那样,我不想再运行另一个查询。 - Константин Манойло
那个另一个查询在哪里? - JPG
def validate_email(self, value): norm_email = value.lower() if User.objects.filter(email=norm_email).exists(): raise serializers.ValidationError("电子邮件不唯一") return norm_email - Константин Манойло
exists() 方法仅一次访问数据库。 - JPG
1
看起来不错。我不知道通过显式定义字段作为序列化器属性将跳过默认检查。谢谢! - Константин Манойло
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接