美多商场 - 用户部分 - 4 用户中心个人信息

1 用户个人中心说明与邮箱激活字段添加

1.1 个人中心介绍

前端访问个人信息页面时,需要向后端请求个人信息。

在本页面中要显示用户的Email邮箱信息,而对于邮箱信息我们要实现对于邮箱的验证功能,并在本页面中显示邮箱是否已验证,如下所示,

个人信息页面

这里有一个邮箱,而邮箱在注册的时候,并没有让用户输入,所以会在这里留一个输入的入口,所以第一次访问基本信息,是这样:

 用户保存之后,还要对邮箱进行校验:

1.2 增加激活字段

这里需要修改User模型类,增加邮箱是否验证的字段。

class User(AbstractUser):
    """
    用户信息
    """
    mobile = models.CharField(max_length=11, unique=True, verbose_name="手机号")
    email_active = models.BooleanField(default=False, verbose_name='邮箱验证状态')

进行数据库迁移

python manage.py makemigrations
python manage.py migrate

2 返回用户跟人信息数据后端接口

2.1 后端逻辑

2.1.1 后端接口设计:

请求方式: GET /user/

请求参数: 无

返回数据: JSON

返回值类型是否必须说明
idint用户id
usernamestr用户名
mobilestr手机号
emailstremail邮箱
email_activebool邮箱是否通过验证

根据接口增加视图逻辑:

而get中的逻辑,其实就是获取详情的逻辑,所以我们可以继承RetrieveModelMixin

还可以直接继承RetrieveAPIView:

继承如下:

序列化器如下:

序列化器代码如下:

class UserDetailSerializer(serializers.ModelSerializer):
    """
    用户详细信息序列化器
    """
    class Meta:
        model = User
        fields = ('id', 'username', 'mobile', 'email', 'email_active')

下来指定查询集:

但是RetrieveAPI中获取详情数据的url是/users/<pk>/而我们设计的接口是/user/。

那只能重写get_object了:

这里是如何获取的user对象呢?

2.1.2 如何获取user

而这里我们压根不用查询数据库,因为我们直接返回登录成功的用户信息即可,关键就是如何获取登录成功的用户。

如何获取用户呢?之前在讲django的时候,说过HttpRequest对象中就有这个已经登录的用户对象user:

但是现在是在类视图的函数中,如何拿到request对象呢?

所以代码如下:

from rest_framework.permissions import IsAuthenticated

class UserDetailView(RetrieveAPIView):
    """
    用户详情
    """
    serializer_class = serializers.UserDetailSerializer
    permission_classes = [IsAuthenticated]

    def get_object(self):
        return self.request.user

注意:访问视图必须要求用户已通过认证(即登录之后)

2.1.3 认证授权

这个视图我们应该是要求用户必须登录之后,才能访问,那怎么办?

来用DRF提供的认证授权机制:

APIView支持的属性如下:

这里就有一个permission_classes,可以知道检测类型。

执行的权限检测类型为:

我们这里选择仅通过认证的用户(也就是仅登录的用户):

权限使用方式如下:

我们选择第二种即可,所以代码如下

2.2 前端请求获取用户个人信息

修改user_center_info.html,增加Vue的变量

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>美多商城-用户中心</title>
    <link rel="stylesheet" type="text/css" href="css/reset.css">
    <link rel="stylesheet" type="text/css" href="css/main.css">
    <script type="text/javascript" src="js/host.js"></script>
    <script type="text/javascript" src="js/vue-2.5.16.js"></script>
    <script type="text/javascript" src="js/axios-0.18.0.min.js"></script>
    <script>
        var user_id = sessionStorage.user_id || localStorage.user_id;
        var token = sessionStorage.token || localStorage.token;
        if (!(user_id && token)) {
            location.href = '/login.html?next=/user_center_info.html';
        }
    </script>
</head>
<body>
    <div id="app" v-cloak>
    <div class="header_con">
        <div class="header">
            <div class="welcome fl">欢迎来到美多商城!</div>
            <div class="fr">
                <div class="login_btn fl">
                    欢迎您:<em>{{ username }}</em>
                    <span>|</span>
                    <a @click="logout">退出</a>
                </div>
                <div class="user_link fl">
                    <span>|</span>
                    <a href="user_center_info.html">用户中心</a>
                    <span>|</span>
                    <a href="cart.html">我的购物车</a>
                    <span>|</span>
                    <a href="user_center_order.html">我的订单</a>
                </div>
            </div>
        </div>        
    </div>

    <div class="search_bar clearfix">
        <a href="index.html" class="logo fl"><img src="images/logo.png"></a>
        <div class="sub_page_name fl">|&nbsp;&nbsp;&nbsp;&nbsp;用户中心</div>
        <form method="get" action="/search.html" class="search_con fr mt40">
            <input type="text" class="input_text fl" name="q" placeholder="搜索商品">
            <input type="submit" class="input_btn fr" name="" value="搜索">
        </form>
    </div>

    <div class="main_con clearfix">
        <div class="left_menu_con clearfix">
            <h3>用户中心</h3>
            <ul>
                <li><a href="user_center_info.html" class="active">· 个人信息</a></li>
                <li><a href="user_center_order.html">· 全部订单</a></li>
                <li><a href="user_center_site.html">· 收货地址</a></li>
                <li><a href="user_center_pass.html">· 修改密码</a></li>
            </ul>
        </div>
        <div class="right_content clearfix">
                <div class="info_con clearfix">
                <h3 class="common_title2">基本信息</h3>
                        <ul class="user_info_list">
                            <li><span>用户名:</span>{{ username }}</li>
                            <li><span>手机号:</span>{{ mobile }}</li>
                            <li>
                                <span>Email:</span>
                                <div v-if="set_email">
                                    <input v-model="email" type="email" name="email">
                                    <input @click="save_email" type="button" name="" value="保 存">
                                    <input @click="set_email=false" type="reset" name="" value="取 消">
                                    <div v-if="email_error">邮箱格式错误</div>
                                </div>
                                <div v-else-if="email">
                                    {{ email }} 
                                    <div v-if="email_active">已验证</div>
                                    <div v-else>
                                        待验证<input @click="save_email" :disabled="send_email_btn_disabled" type="button" :value="send_email_tip">
                                    </div>
                                </div>
                                <div v-else>
                                    <input @click="set_email=true" type="button" name="" value="设 置">
                                </div>
                            </li>            
                        </ul>
                </div>

                <h3 class="common_title2">最近浏览</h3>
                <div class="has_view_list">
                    <ul class="goods_type_list clearfix">
                <li>
                    <a href="detail.html"><img src="images/goods/goods003.jpg"></a>
                    <h4><a href="detail.html">360手机 N6 Pro 全网通</a></h4>
                    <div class="operate">
                        <span class="prize">¥2699.00</span>
                        <span class="unit">台</span>
                        <a href="#" class="add_goods" title="加入购物车"></a>
                    </div>
                </li>

                <li>
                    <a href="#"><img src="images/goods/goods004.jpg"></a>
                    <h4><a href="#">360手机 N6 Pro 全网通</a></h4>
                    <div class="operate">
                        <span class="prize">¥2699.00</span>
                        <span class="unit">台</span>
                        <a href="#" class="add_goods" title="加入购物车"></a>
                    </div>
                </li>

                <li>
                    <a href="#"><img src="images/goods/goods005.jpg"></a>
                    <h4><a href="#">360手机 N6 Pro 全网通</a></h4>
                    <div class="operate">
                        <span class="prize">¥2699.00</span>
                        <span class="unit">台</span>
                        <a href="#" class="add_goods" title="加入购物车"></a>
                    </div>
                </li>

                <li>
                    <a href="#"><img src="images/goods/goods006.jpg"></a>
                    <h4><a href="#">360手机 N6 Pro 全网通</a></h4>
                    <div class="operate">
                        <span class="prize">¥2699.00</span>
                        <span class="unit">台</span>
                        <a href="#" class="add_goods" title="加入购物车"></a>
                    </div>
                </li>

                <li>
                    <a href="#"><img src="images/goods/goods007.jpg"></a>
                    <h4><a href="#">急速路由</a></h4>
                    <div class="operate">
                        <span class="prize">¥64.5</span>
                        <span class="unit">6.45/500g</span>
                        <a href="#" class="add_goods" title="加入购物车"></a>
                    </div>
                </li>
            </ul>
        </div>
            </div>
    </div>
    <div class="footer">
        <div class="foot_link">
            <a href="#">关于我们</a>
            <span>|</span>
            <a href="#">联系我们</a>
            <span>|</span>
            <a href="#">招聘人才</a>
            <span>|</span>
            <a href="#">友情链接</a>        
        </div>
        <p>CopyRight © 2016 北京美多商业股份有限公司 All Rights Reserved</p>
        <p>电话:010-****888    京ICP备*******8号</p>
    </div>
</div>
<script type="text/javascript" src="js/user_center_info.js"></script>
</body>
</html>

在js目录中新建user_center_info.js

var vm = new Vue({
    el: '#app',
    data: {
        host,
        user_id: sessionStorage.user_id || localStorage.user_id,
        token: sessionStorage.token || localStorage.token,
        username: '',
        mobile: '',
        email: '',
        email_active: false,
        set_email: false,
        send_email_btn_disabled: false,
        send_email_tip: '重新发送验证邮件',
        email_error: false,
        histories: []
    },
    mounted: function(){
        // 判断用户的登录状态
        if (this.user_id && this.token) {
            axios.get(this.host + '/user/', {
                    // 向后端传递JWT token的方法
                    headers: {
                        'Authorization': 'JWT ' + this.token
                    },
                    responseType: 'json',
                })
                .then(response => {
                    // 加载用户数据
                    this.user_id = response.data.id;
                    this.username = response.data.username;
                    this.mobile = response.data.mobile;
                    this.email = response.data.email;
                    this.email_active = response.data.email_active;
                })
                .catch(error => {
                    if (error.response.status==401 || error.response.status==403) {
                        location.href = '/login.html?next=/user_center_info.html';
                    }
                });
        } else {
            location.href = '/login.html?next=/user_center_info.html';
        }
    },
    methods: {
        // 退出
        logout: function(){
            sessionStorage.clear();
            localStorage.clear();
            location.href = '/login.html';
        },
        // 保存email
        save_email: function(){

        }
    }
});

3 邮件与验证

业务说明:

在用户中心页面中,我们允许用户设置邮箱

设置邮箱

当用户点击保存后,我们会向用户发送邮件以验证邮箱的有效性。

验证邮件

为了避免用户未收到验证邮箱,我们提供“重新发送验证邮件”按钮允许用户重新发送邮件。

重新发送验证邮件

邮箱验证成功,显示已验证。

邮箱已验证

技术说明:

在邮件中提供的激活链接地址,为了能区分是哪个用户在进行邮箱验证,需要在链接中包含用户和邮箱的识别信息,如user_id和email数据,但是基于安全性的考虑,不能将这两个数据直接暴露在邮件链接中,而是需要进行隐藏和签名处理(能够检测出是否修改过链接数据)。可以使用前面学过的itsdangerous对user_id和email数据进行处理,生成token作为链接的参数。

4 使用Django发送邮件

我们需要在保存邮箱的同时,发送一封邮件,那我们需要先分析一下如何发送邮件。

那我们如何给发送邮件服务器发送一个smtp协议的请求,去发送邮件呢?

我们需要找一个发送邮件的服务器,我们以163为例。

Django中内置了邮件发送功能,被定义在django.core.mail模块中。发送邮件需要使用SMTP服务器,常用的免费服务器有:163126QQ,下面以163邮件为例。

1)注册163邮箱itcast88,登录后设置。

发送邮件

2)在新页面中点击“客户端授权密码”,勾选“开启”,弹出新窗口填写手机验证码。

发送邮件

3)填写授权码。

发送邮件

4)提示开启成功。

发送邮件

5) 在Django配置文件中,设置邮箱的配置信息

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
#发送邮件的邮箱
EMAIL_HOST_USER = 'itcast88@163.com'
#在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'python808'
#收件人看到的发件人
EMAIL_FROM = 'python<itcast88@163.com>'

6) 使用Django提供的模块发送邮件

django.core.mail模块提供了send_mail来发送邮件。

send_mail(subjectmessagefrom_emailrecipient_list,html_message=None)

  • subject 邮件标题
  • message 普通邮件正文, 普通字符串
  • from_email 发件人
  • recipient_list 收件人列表
  • html_message 多媒体邮件正文,可以是html字符串

例如:

msg='<a href="http://www.itcast.cn/subject/pythonzly/index.shtml" target="_blank">点击激活</a>'
send_mail('注册激活','',settings.EMAIL_FROM, ['itcast88@163.com'], html_message=msg)

5 保存邮箱并发送验证邮件

5.1 保存邮箱后端接口实现

接下来处理保存邮箱,界面如下:

后端接口设计:

请求方式:PUT /email/

请求参数: JSON 或 表单

参数类型是否必须说明
emailstrEmail邮箱

返回数据: JSON

返回值类型是否必须说明
idint用户id
emailstrEmail邮箱

具体逻辑分析如下:

上述的逻辑其实就是更新的一个逻辑,所以我们可以继承UpdateModelMixin:

源码如下,大家可以看下update方法,做的事情与我们分析的是一致的:

当然我们会直接继承UpdateApiView:

继承之后代码如下:

注意:这里为啥要重写get_object,因为我们的url是不接受pk参数的,所以UpdateApiView无法确定我们要更新哪个模型类,所以我们要重写get_object,告诉他更新哪个模型类。我们这里更新的是user模型类。

序列化器如下:

序列化器中就两个字段id和email,关于这俩字段的方向问题,都不用处理。

id,默认只能做序列化操作。

email,序列化器和反序列化都要做,也是默认。

注意:这里咱们还没有处理到更新邮箱的逻辑,我们在下边发送邮件的时候,处理。

5.2 定义发送邮件的任务

配置Email:

celery_tasks新建任务email:

配置任务:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值