Django+Vue开发生鲜电商平台之7.用户登录和注册功能(下)

简介: 基于DRF的前后端分离登录与单独使用Django登录的原理不同,不再需要CSRF验证,DRF提供了许多开箱即用的身份验证方案,并且还允许实现自定义方案。

4.密码设置的多种方式

进一步完善序列化如下:

class UserRegSerializer(serializers.ModelSerializer):
    '''用户序列化'''
    code = serializers.CharField(max_length=4, min_length=4, label='验证码',
                                 help_text='验证码',
                                 error_messages={
                                     'required': '请输入验证码',
                                     'blank': '请输入验证码',
                                     'max_length': '请输入4位验证码',
                                     'min_length': '请输入4位验证码'
                                 })
    username = serializers.CharField(required=True, allow_blank=False,  label='用户名', validators=[UniqueValidator(queryset=User.objects.filter(is_delete=False), message='用户已经存在')])
    password = serializers.CharField(label='密码', style={'input_type': 'password'})
    def validate_code(self, code):
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
        # 验证验证码是否存在
        if verify_records:
            last_record = verify_records[0]
            five_minute_ago = datetime.now() - timedelta(minutes=5)
            # 验证验证码是否过期
            if five_minute_ago > last_record.add_time:
                raise serializers.ValidationError('验证码已过期,请重新验证')
            # 验证验证码是否正确
            if last_record.code != code:
                raise serializers.ValidationError('验证码错误')
        else:
            raise serializers.ValidationError('数据有误,请重新验证')
    def validate(self, attrs):
        attrs['mobile'] = attrs['username']
        del attrs['code']
        return attrs
    class Meta:
        model = User
        fields = ['username', 'code', 'mobile', 'password']

此时再访问模拟注册如下:

image.jpeg

显然,报错如下:

raise type(exc)(msg)
AttributeError: Got AttributeError when attempting to get a value for field `code` on serializer `UserRegSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `UserProfile` instance.
Original exception text was: 'UserProfile' object has no attribute 'code'.

报错提示很明显,UserProfile没有code属性。具体来说,这是因为Meta中指定了fields = ['username', 'code', 'mobile', 'password'],包含code字段,而在验证时为了判断验证码的正误而临时加入code字段,但是在validate(attrs)又将其删去,导致在序列化时找不到code字段,因此出错,这是需要将字段的write_only设置True,以确保在更新或创建实例时可以使用该字段,但是在序列化表示形式时不包括该字段。

同时查询用户表如下:

+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
| id | password                                                                       | last_login                 | is_superuser | username    | first_name | last_name | is_staff | is_active | date_joined                | name | birthday | gender | mobile      | email       | is_delete |
+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
|  1 | pbkdf2_sha256$180000$wpfCm77Dcpee$rHfFjBNZ2SzLLHdd0ZtbiIRqNB86VvgwTJv6ZCXTbfk= | 2020-07-28 20:11:10.453289 |            1 | admin       |            |           |        1 |         1 | 2020-07-20 10:12:43.787964 | NULL | NULL     | female |             | 123@123.com |         0 |
|  2 | pbkdf2_sha256$180000$VqEN1rdsS4ts$hgqzLLzxvIk3au1osUB/yrJA5ffFubE87gRBumUAqUE= | NULL                       |            1 | admin2      |            |           |        1 |         1 | 2020-07-27 18:46:54.826360 | NULL | NULL     | female |             | 456@123.com |         0 |
|  4 | admin12345                                                                     | NULL                       |            0 | 13388888888 |            |           |        0 |         1 | 2020-07-28 20:24:01.808609 | NULL | NULL     | female | 13388888888 | NULL        |         0 |
+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
3 rows in set (0.01 sec)

显然,刚刚的用户已经保存到用户表中,但是密码为明文,存在很大的风险,需要进行加密设置,可以重载create(validated_data)实现密码设置即可。除此之外,为了password字段不返回前端,也需要为其加write_only属性,serializers.py完善如下:

class UserRegSerializer(serializers.ModelSerializer):
    '''用户序列化'''
    code = serializers.CharField(max_length=4, min_length=4, label='验证码', write_only=True,
                                 help_text='验证码',
                                 error_messages={
                                     'required': '请输入验证码',
                                     'blank': '请输入验证码',
                                     'max_length': '请输入4位验证码',
                                     'min_length': '请输入4位验证码'
                                 })
    username = serializers.CharField(required=True, allow_blank=False,  label='用户名', validators=[UniqueValidator(queryset=User.objects.filter(is_delete=False), message='用户已经存在')])
    password = serializers.CharField(label='密码',  write_only=True, style={'input_type': 'password'})
    def create(self, validated_data):
        user = super(UserRegSerializer, self).create(validated_data=validated_data)
        user.set_password(validated_data['password'])
        user.save()
        return user
    def validate_code(self, code):
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
        # 验证验证码是否存在
        if verify_records:
            last_record = verify_records[0]
            five_minute_ago = datetime.now() - timedelta(minutes=5)
            # 验证验证码是否过期
            if five_minute_ago > last_record.add_time:
                raise serializers.ValidationError('验证码已过期,请重新验证')
            # 验证验证码是否正确
            if last_record.code != code:
                raise serializers.ValidationError('验证码错误')
        else:
            raise serializers.ValidationError('数据有误,请重新验证')
    def validate(self, attrs):
        attrs['mobile'] = attrs['username']
        del attrs['code']
        return attrs
    class Meta:
        model = User
        fields = ['username', 'code', 'mobile', 'password']

此时再进行测试如下:


2345_image_file_copy_37.jpg

显然,测试成功,在提交之后返回数据,查询用户表如下:

+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
| id | password                                                                       | last_login                 | is_superuser | username    | first_name | last_name | is_staff | is_active | date_joined                | name | birthday | gender | mobile      | email       | is_delete |
+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
|  1 | pbkdf2_sha256$180000$wpfCm77Dcpee$rHfFjBNZ2SzLLHdd0ZtbiIRqNB86VvgwTJv6ZCXTbfk= | 2020-07-28 20:11:10.453289 |            1 | admin       |            |           |        1 |         1 | 2020-07-20 10:12:43.787964 | NULL | NULL     | female |             | 123@123.com |         0 |
|  2 | pbkdf2_sha256$180000$VqEN1rdsS4ts$hgqzLLzxvIk3au1osUB/yrJA5ffFubE87gRBumUAqUE= | NULL                       |            1 | admin2      |            |           |        1 |         1 | 2020-07-27 18:46:54.826360 | NULL | NULL     | female |             | 456@123.com |         0 |
|  4 | admin12345                                                                     | NULL                       |            0 | 13388888888 |            |           |        0 |         1 | 2020-07-28 20:24:01.808609 | NULL | NULL     | female | 13388888888 | NULL        |         0 |
|  5 | pbkdf2_sha256$180000$dKdR8lvqcymO$OZunKajLJo6q+b3ub+NYNTuKNyOzlz9wGN08DYobUrY= | NULL                       |            0 | 13377777777 |            |           |        0 |         1 | 2020-07-28 20:55:39.193938 | NULL | NULL     | female | 13377777777 | NULL        |         0 |
+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
4 rows in set (0.00 sec)

显然,新注册的用户密码位密文,而不再是明文。

除了用以上方式实现密码设置,还可以通过Django信号量实现,具体可查看https://docs.djangoproject.com/en/1.10/ref/signals/。其中一类信号是模型信号,django.db.models.signals模块定义了模型系统发送的一组信号,对模型进行操作后,Django会发出全局信号,捕捉到之后可以加入需要的业务逻辑,具体包括pre_init、post_init、pre_save和post_save等,这里我们使用post_save信号实现密码设置。

在apps/users下创建signals.py如下:

from django.db.models.signals import post_save
from django.contrib.auth import get_user_model
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
User = get_user_model()
@receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        password = instance.password
        instance.set_password(password)
        instance.save()

在apps/users/apps.py中进行配置如下:

from django.apps import AppConfig
class UsersConfig(AppConfig):
    name = 'users'
    verbose_name = '用户管理'
    def ready(self):
        import users.signals

serializers.py去掉设置密码的逻辑如下:

class UserRegSerializer(serializers.ModelSerializer):
    '''用户序列化'''
    code = serializers.CharField(max_length=4, min_length=4, label='验证码', write_only=True,
                                 help_text='验证码',
                                 error_messages={
                                     'required': '请输入验证码',
                                     'blank': '请输入验证码',
                                     'max_length': '请输入4位验证码',
                                     'min_length': '请输入4位验证码'
                                 })
    username = serializers.CharField(required=True, allow_blank=False,  label='用户名', validators=[UniqueValidator(queryset=User.objects.filter(is_delete=False), message='用户已经存在')])
    password = serializers.CharField(label='密码',  write_only=True, style={'input_type': 'password'})
    def validate_code(self, code):
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
        # 验证验证码是否存在
        if verify_records:
            last_record = verify_records[0]
            five_minute_ago = datetime.now() - timedelta(minutes=5)
            # 验证验证码是否过期
            if five_minute_ago > last_record.add_time:
                raise serializers.ValidationError('验证码已过期,请重新验证')
            # 验证验证码是否正确
            if last_record.code != code:
                raise serializers.ValidationError('验证码错误')
        else:
            raise serializers.ValidationError('数据有误,请重新验证')
    def validate(self, attrs):
        attrs['mobile'] = attrs['username']
        del attrs['code']
        return attrs
    class Meta:
        model = User
        fields = ['username', 'code', 'mobile', 'password']

此时在后台进行测试如下:

2345_image_file_copy_38.jpg

前台测试如下:

2345_image_file_copy_39.jpg

再查询数据库,如下:

+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
| id | password                                                                       | last_login                 | is_superuser | username    | first_name | last_name | is_staff | is_active | date_joined                | name | birthday | gender | mobile      | email       | is_delete |
+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
|  1 | pbkdf2_sha256$180000$wpfCm77Dcpee$rHfFjBNZ2SzLLHdd0ZtbiIRqNB86VvgwTJv6ZCXTbfk= | 2020-07-28 20:11:10.453289 |            1 | admin       |            |           |        1 |         1 | 2020-07-20 10:12:43.787964 | NULL | NULL     | female |             | 123@123.com |         0 |
|  2 | pbkdf2_sha256$180000$VqEN1rdsS4ts$hgqzLLzxvIk3au1osUB/yrJA5ffFubE87gRBumUAqUE= | NULL                       |            1 | admin2      |            |           |        1 |         1 | 2020-07-27 18:46:54.826360 | NULL | NULL     | female |             | 456@123.com |         0 |
|  4 | admin12345                                                                     | NULL                       |            0 | 13388888888 |            |           |        0 |         1 | 2020-07-28 20:24:01.808609 | NULL | NULL     | female | 13388888888 | NULL        |         0 |
|  5 | pbkdf2_sha256$180000$dKdR8lvqcymO$OZunKajLJo6q+b3ub+NYNTuKNyOzlz9wGN08DYobUrY= | NULL                       |            0 | 13377777777 |            |           |        0 |         1 | 2020-07-28 20:55:39.193938 | NULL | NULL     | female | 13377777777 | NULL        |         0 |
|  6 | pbkdf2_sha256$180000$h6Daqay8tHp3$7Cuw+iigsrqBFldUJybFt8hq5SDwjiwxXhgRMYvs6iw= | NULL                       |            0 | 13366666666 |            |           |        0 |         1 | 2020-07-28 21:28:27.957466 | NULL | NULL     | female | NULL        |             |         0 |
|  7 | pbkdf2_sha256$180000$JM8j2c0fl81i$DesmherPz0ZUC+orr5kDREVmDJPTc4ahb4vL3Zd/s5s= | NULL                       |            0 | 13355555555 |            |           |        0 |         1 | 2020-07-28 21:31:56.062071 | NULL | NULL     | female | 13355555555 | NULL        |         0 |
+----+--------------------------------------------------------------------------------+----------------------------+--------------+-------------+------------+-----------+----------+-----------+----------------------------+------+----------+--------+-------------+-------------+-----------+
6 rows in set (0.00 sec)

显然,用户均创建成功,并且密码为密文,说明信号成功实现了密码设置。

四、Vue实现注册功能

现在实现前端注册功能,前端src/views/register下定义了注册的组件register.vue,如下:

isRegister(){
    var that = this;
    register({
        password:that.password,
        username:that.mobile ,
        code:that.code,
    }).then((response)=> {
      cookie.setCookie('name',response.data.username,7);
      cookie.setCookie('token',response.data.token,7)
      //存储在store
      // 更新store数据
      that.$store.dispatch('setInfo');
      //跳转到首页页面
      this.$router.push({ name: 'index'})
  })
  .catch(function (error) {
    that.error.mobile = error.username?error.username[0]:'';
    that.error.password = error.password?error.password[0]:'';
    that.error.username = error.mobile?error.mobile[0]:'';
    that.error.code = error.code?error.code[0]:'';
  });
},

因为一般在注册成功之后会有两种情况:

一种是注册成功后直接自动登录并跳转到指定页,这里采用的就是这种方式;

另一种是注册后不自动登录,但是跳转到登录页或其他页面,需要自己手动登录,这时只需要注释掉

cookie.setCookie('name',response.data.username,7);
cookie.setCookie('token',response.data.token,7)
//存储在store
// 更新store数据
that.$store.dispatch('setInfo');

部分即可。

这里传递了注册需要用到的3个字段,并且使用了register接口,在api.js中定义修改如下:

//注册
export const register = parmas => { return axios.post(`${local_host}/users/`, parmas) }

在实现注册后自动登录的效果时,还需要设置token,但是后端还并未设置token接口,需要进行配置,views.py配置如下:

class UserViewSet(CreateModelMixin, viewsets.GenericViewSet):
    '''用户'''
    serializer_class = UserRegSerializer
    queryset = User.objects.filter(is_delete=False)
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)
        re_dict = serializer.data
        payload = jwt_payload_handler(user)
        re_dict['token'] = jwt_encode_handler(payload)
        re_dict['name'] = user.name if user.name else user.username
        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
    def perform_create(self, serializer):
         return serializer.save()

进行测试如下:

image.jpeg

显然,已经可以正常注册并登录。

可以看到,在登录之后可以退出,在src/views/head/head.vue中实现逻辑如下:

<a @click="loginOut">退出</a>
...
loginOut(){
    // this.$http.get('/getMenu')
    //     .then((response)=> {
            //跳转到登录
            this.$router.push({ name: 'login' })
    //     })
    //     .catch(function (error) {
    //       console.log(error);
    // });
},

src/views/head/shophead.vue如下:

<a @click="loginOut">退出</a>
...
loginOut(){
        cookie.delCookie('token');
        cookie.delCookie('name');
        //重新触发store
        //更新store数据
        this.$store.dispatch('setInfo');
        //跳转到登录
        this.$router.push({name: 'login'})
      },

显然,退出登录的逻辑是cookie中删除token和name,并重定向到登录页。

相关文章
|
26天前
|
存储 缓存 前端开发
Django 后端架构开发:存储层调优策略解析
Django 后端架构开发:存储层调优策略解析
36 2
|
23天前
|
JavaScript API
vue 批量自动引入并注册组件或路由
vue 批量自动引入并注册组件或路由
|
26天前
|
负载均衡 应用服务中间件 网络安全
Django后端架构开发:Nginx服务优化实践
Django后端架构开发:Nginx服务优化实践
36 2
|
26天前
|
消息中间件 存储 监控
Django后端架构开发:Celery异步调优,任务队列和调度
Django后端架构开发:Celery异步调优,任务队列和调度
40 1
|
21天前
|
开发者 Android开发 iOS开发
Xamarin开发者的神器!揭秘你绝不能错过的插件和工具,让你的开发效率飞跃式提升
【8月更文挑战第31天】Xamarin.Forms 是一个强大的框架,让开发者通过单一共享代码库构建跨平台移动应用,支持 iOS、Android 和 Windows。使用 C# 和 XAML,它简化了多平台开发流程,保持一致的用户体验。本指南通过创建一个简单的 “HelloXamarin” 应用介绍 Xamarin.Forms 的基本功能和工作原理。首先配置 Visual Studio 开发环境,然后创建并运行一个包含标题、按钮和消息标签的示例应用,展示如何定义界面布局及处理按钮点击事件。这帮助开发者快速入门 Xamarin.Forms,提高跨平台应用开发效率。
31 0
|
21天前
|
C++ Python
Django视图函数VS类视图:如何选择最适合你的开发方式?
【8月更文挑战第31天】本文对比了Django中的函数视图和类视图。函数视图直接处理HTTP请求和响应,灵活且易于维护,适用于简单业务逻辑;类视图基于Python类,提供更丰富的功能和更高的灵活性,适合处理复杂业务逻辑。选择哪种视图取决于具体需求,合理使用两者可帮助你构建高效且易维护的Django应用。
12 0
|
22天前
|
JavaScript 前端开发 开发者
Vue.js的未来已来:掌握最新功能,驾驭前端开发的新浪潮!
【8月更文挑战第30天】Vue.js作为前端开发领域的明星框架,凭借其轻量级、响应式及组件化特性,深受全球开发者喜爱。它持续进化,引入新功能并优化性能,如提升渲染速度和减小打包体积,增强TypeScript支持,简化组件编写流程,进一步提升应用响应性和加载速度,改善开发者体验。活跃的社区和丰富的开源项目如“vuejs-challenges”推动其不断发展。未来,Vue.js将探索更多新特性,如宏和非虚拟DOM模式,巩固其在现代前端框架中的领先地位。
34 0
|
26天前
|
存储 缓存 关系型数据库
Django后端架构开发:缓存机制,接口缓存、文件缓存、数据库缓存与Memcached缓存
Django后端架构开发:缓存机制,接口缓存、文件缓存、数据库缓存与Memcached缓存
29 0
|
1月前
|
机器学习/深度学习 数据采集 数据可视化
基于爬虫和机器学习的招聘数据分析与可视化系统,python django框架,前端bootstrap,机器学习有八种带有可视化大屏和后台
本文介绍了一个基于Python Django框架和Bootstrap前端技术,集成了机器学习算法和数据可视化的招聘数据分析与可视化系统,该系统通过爬虫技术获取职位信息,并使用多种机器学习模型进行薪资预测、职位匹配和趋势分析,提供了一个直观的可视化大屏和后台管理系统,以优化招聘策略并提升决策质量。
|
1月前
|
搜索推荐 前端开发 数据可视化
【优秀python web毕设案例】基于协同过滤算法的酒店推荐系统,django框架+bootstrap前端+echarts可视化,有后台有爬虫
本文介绍了一个基于Django框架、协同过滤算法、ECharts数据可视化以及Bootstrap前端技术的酒店推荐系统,该系统通过用户行为分析和推荐算法优化,提供个性化的酒店推荐和直观的数据展示,以提升用户体验。