Skip to main content

实现邮箱验证唯一的功能

在本节课中,我们将解决一个关于用户注册的重要问题:确保邮箱在数据表中是唯一的。之前我们通过使用djoser的自定义注册用户接口实现了用户的注册和邮箱激活,但我们发现email字段在数据库中并不是唯一的,导致同一个邮箱可以被用来注册多个不同的用户名。

使用已经注册的邮箱,再次注册用户,如下图所示。 图31-同一个邮箱被多人注册

注册成功后,查看auth_user表,2个账号邮箱相同。如下图。

图31-邮箱相同

要解决这个问题,我们将采用以下步骤:

检查Djoser的配置和原始代码:我们首先查看了Djoser的配置和原始代码,了解其工作原理,尤其是它如何处理用户创建和序列化。

查看djanoser源码的conf.py文件,创建用户使用的是UserCreateSerializer, 如下图所示。

图31-djoser配置文件

自定义邮箱验证逻辑

  • 我们在Djoser的基础上扩展了自己的逻辑,主要通过修改序列化器(serializer)来实现。
  • 我们创建了一个新的序列化器,在这个序列化器中,我们继承了Djoser的UserCreateSerializer并添加了对email字段的唯一性验证。

配置settings.py文件,找到DJOSER配置,代码如下:

dx_movie/dx_movie/settings.py
DJOSER = {
'USER_ID_FIELD': 'username',
'LOGIN_FIELD': 'email',
'ACTIVATION_URL': 'activate/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
'SEND_CONFIRMATION_EMAIL': True,
'SERIALIZERS': {
'user_create': 'account.serializers.CustomUserCreateSerializer',
},

}

上面代码中,指定了'user_create', 替换了默认的djoser配置。但是'account.serializers.CustomUserCreateSerializer'还不存在,需要我们去创建。 接下里,创建account应用,它是和用户相关的应用,, 命令如下:

python manage.py startapp account

创建完应用后,追加到settings.py中的INSTALLED_APPS, 代码如下:

dx_movie/dx_movie/settings.py
INSTALLED_APPS = [
...
account
]

接下来,在account应用下创建一个名为serializers.py的文件,创建一个类CustomUserCreateSerializer,继承原始的djoser中的UserCreateSerializer。代码如下:

dx_movie/account/serializers.py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from djoser.serializers import UserCreateSerializer
from django.contrib.auth.models import User


class CustomUserCreateSerializer(UserCreateSerializer):
email = serializers.EmailField(
validators = [UniqueValidator(queryset=User.objects.all())]
)

class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
代码解析

您提供的代码片段涉及Django框架和Django REST framework,特别是在处理用户注册和数据序列化方面。这里的代码主要扩展了djoser库的用户创建序列化器来自定义邮箱字段的唯一性验证。以下是对代码的逐行解释:

  1. 导入语句:
    • from rest_framework import serializers: 从Django REST framework导入序列化器功能,这是处理HTTP请求和响应中的数据转换的关键组件。

    • from rest_framework.validators import UniqueValidator: 导入用于字段唯一性验证的验证器,用于确保例如邮箱地址不会在数据库中重复。

    • from djoser.serializers import UserCreateSerializer: 从djoser库(一个用于Django REST framework的身份验证库)导入用户创建序列化器,这个序列化器提供了用户注册时数据的序列化。

    • from django.contrib.auth.models import User: 导入Django内置的用户模型,这是Django认证系统的一部分,用于表示用户。

  2. 自定义用户创建序列化器CustomUserCreateSerializer:
    • 继承自djoserUserCreateSerializer,这是基于Django用户模型的用户创建序列化器。
    • 在这个序列化器内,email字段被定义为EmailField,并附加了CustomUniqueValidator。这意味着当尝试创建新用户时,将检查邮箱的唯一性,并在邮箱已存在的情况下显示自定义错误消息。
    • Meta类定义了序列化器用于序列化和反序列化的模型(在此为User模型)和字段(id, username, email, password)。这告诉序列化器处理请求和响应时应该包含哪些字段。

总体来说,这段代码的目的是在用户注册过程中提供一个自定义的用户创建序列化器,主要改进在于当用户尝试使用已存在的邮箱注册时,提供更清晰、友好的错误消息。通过扩展djoserUserCreateSerializer和自定义唯一性验证器,开发者能够提供更精细化的用户体验。

运行效果如下图所示。

图31-字段必须唯一

"该字段必须唯一"这个提示对用户来说体验并不好,我们需要明确告诉用户具体是什么错误。接下来,我们自己定义一个类,让其继承UniqueValidator方法,但是修改它的提示内容。修改后的代码如下:

dx_movie/account/serializers.py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from djoser.serializers import UserCreateSerializer
from django.contrib.auth.models import User

class CustomUniqueValidator(UniqueValidator):
def __call__(self, value, serializer_field):
self.message = '邮箱 %s 已经存在' % value
return super().__call__(value, serializer_field)


class CustomUserCreateSerializer(UserCreateSerializer):
email = serializers.EmailField(
validators = [CustomUniqueValidator(queryset=User.objects.all())]
)

class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
代码解析

自定义唯一性验证器CustomUniqueValidator**:

  • 这个类继承了UniqueValidator,用于验证一个字段值在数据库中的唯一性。
  • __call__方法被重写,当验证器被调用时执行。这里的自定义主要是为了改变默认的错误信息。如果值(在这个场景中是邮箱)已经存在于数据库中,它会设置一个自定义的错误信息('邮箱 %s 已经存在')。

图31-提示具体错误信息

通过这些步骤,我们成功实现了邮箱验证的唯一性功能,避免了一个邮箱被用来注册多个用户账号的问题。这不仅增强了数据的完整性和安全性,也提供了更好的用户体验。