验证唯一的电子邮件 Django Rest 框架序列化器

Кон*_*йло 4 django django-serializer django-rest-framework

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

    def validate_email(self, value):
        return value.lower()
Run Code Online (Sandbox Code Playgroud)

但是,看起来此方法在序列化程序检查数据库中是否存在该值后返回该值。这是一个例子:

如果我尝试使用 user@gmail.com 创建用户并且该用户已经存在,它将返回“用户已存在”,这是预期的。但是,如果我使用 User@gmail.com 运行,它将首先运行 SQL 请求并检查 User@gmail.com != user@gmail.com,然后它将尝试使用 user@gmail.com 创建一个新实例因为它是从 validate_email() 返回的,并且会引发 IntegrityError,因为它变成了已经在数据库中的 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
Run Code Online (Sandbox Code Playgroud)

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

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

JPG*_*JPG 6

使用iexact--(django doc)查找

User.objects.filter(email__iexact=norm_email).exists()
Run Code Online (Sandbox Code Playgroud)

更新

DRF 在后台查询以检查唯一约束,因为我们在模型字段中设置了unique=True。为了避免这种情况,我们需要在序列化器中显式email定义该字段,这会绕过唯一检查验证

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',)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)