【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_框架

本教程的知识点为: 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片 1. 后端接口设计: 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送 判断帐号是否存在 1. 判断用户名是否存在 后端接口设计: 用户部分 JWT 什么是JWT 起源 传统的session认证 用户部分 登录 1. 业务说明 2. 后端接口设计 3. 后端实现 登录 使用登录的流程 创建模型类 urllib使用说明 登录回调处理 登录 使用登录的流程 创建模型类 urllib使用说明 绑定用户身份接口 邮件与验证 学习目标: 业务说明: 技术说明: 保存邮箱并发送验证邮件 省市区地址查询 数据库建表 说明 页面静态化 注意 定时任务 安装 部分 详情页 异步任务的触发 。 后端接口设计 收货地址 使用缓存 安装 使用方法 为省市区视图添加缓存 数据库表设计 表结构 数据表结构 首页数据表结构 Docker使用 Docker简介 用户浏览历史记录 1. 保存 后端接口设计 后端实现 搜索 1. 需求分析 2. 搜索引擎原理 3. Elasticsearch 部分 业务需求分析 技术实现 数据存储设计 1. Redis保存已登录用户 商品部分 业务需求分析 技术实现 查询数据 1. 后端接口设计 部分 业务需求分析 技术实现 登录合并 修改登录视图 部分 保存 1. 后端接口设计 2. 后端实现 保存的思路 创建数据库模型类 接入 开发平台登录 沙箱环境 Xadmin 1. 安装 2. 使用 站点的全局配置 站点Model管理。 在Ubuntu中安装 2. 启动与停止 3. 镜像操作 端与自定义文件存储系统 1. 的Python客户端 安装 使用。

完整笔记资料代码: https://gitee.com/yinuo112/Backend/tree/master/Django/django美多商城项目完整开发4.0/note.md

感兴趣的小伙伴可以自取哦~

全套教程部分目录:

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_django_02

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_后端_03


部分文件图片:

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_后端_04

邮件与验证

学习目标:
  • 使用Django发送邮件的方法
  • 邮件激活的机制
业务说明:

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

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_python_05

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

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_后端_06

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

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_django_07

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

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_python_08

技术说明:

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

保存邮箱并发送验证邮件

后端接口设计:

请求方式:PUT /email/

请求参数: JSON 或 表单

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

返回数据: JSON

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

在users/serializers.py中新建序列化器,用户验证用户提交的邮箱信息。

class EmailSerializer(serializers.ModelSerializer):
    """
    邮箱序列化器
    """
    class Meta:
        model = User
        fields = ('id', 'email')
        extra_kwargs = {
            'email': {
                'required': True
            }
        }

    def update(self, instance, validated_data):
        instance.email = validated_data['email']
        instance.save()
        return instance
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

在users/views.py中创建新视图,用于保存用户的邮箱信息,注意需要用户登录通过认证后。

from rest_framework.permissions import IsAuthenticated

class EmailView(UpdateAPIView):
    """
    保存用户邮箱
    """
    permission_classes = [IsAuthenticated]
    serializer_class = serializers.EmailSerializer

    def get_object(self, *args, **kwargs):
        return self.request.user
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

设置路由信息

url(r'^emails/$', views.EmailView.as_view()),  # 设置邮箱
  • 1.
补充发送验证邮件

在保存邮箱的时候,需要向用户发送验证邮件,我们将发送邮件的工作放到celery中异步执行。

在celery*tasks目录中新建email目录和email/*_init**.py文件和email/tasks.py文件

email/tasks.py文件中是实现发送邮件的异步任务

from celery_tasks.main import celery_app
from django.core.mail import send_mail
from django.conf import settings


@celery_app.task(name='send_verify_email')
def send_verify_email(to_email, verify_url):
    """
    发送验证邮箱邮件
    :param to_email: 收件人邮箱
    :param verify_url: 验证链接
    :return: None
    """
    subject = "美多商城邮箱验证"
    html_message = '<p>尊敬的用户您好!</p>' \
                   '<p>感谢您使用美多商城。</p>' \
                   '<p>您的邮箱为:%s 。请点击此链接激活您的邮箱:</p>' \
                   '<p><a rel="nofollow" href="%s">%s<a></p>' % (to_email, verify_url, verify_url)
    send_mail(subject, "", settings.EMAIL_FROM, [to_email], html_message=html_message)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
注意

在发送邮件的异步任务中,需要用到django的配置文件,所以我们需要修改celery的启动文件main.py,在其中指明celery可以读取的django配置文件,并且注册添加email的任务

from celery import Celery

  
  
# 为celery使用django配置文件进行设置
  
  
import os
if not os.getenv('DJANGO_SETTINGS_MODULE'):
    os.environ['DJANGO_SETTINGS_MODULE'] = 'meiduo_mall.settings.dev'

  
  
# 创建celery应用
  
  
celery_app = Celery('meiduo')

  
  
# 导入celery配置
  
  
celery_app.config_from_object('celery_tasks.config')

  
  
# 自动注册celery任务
  
  
celery_app.autodiscover_tasks(['celery_tasks.sms', 'celery_tasks.email'])
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
在User模型类中定义生成验证邮箱链接的方法

邮箱的激活链接是用户点击时会访问的网址,我们让用户点击时进入到success_verify_email.html页面。

def generate_verify_email_url(self):
        """
        生成验证邮箱的url
        """
        serializer = TJWSSerializer(settings.SECRET_KEY, expires_in=constants.VERIFY_EMAIL_TOKEN_EXPIRES)
        data = {'user_id': self.id, 'email': self.email}
        token = serializer.dumps(data).decode()
        verify_url = ' + token
        return verify_url
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
修改EmailSerializer序列化器的update方法,增加发送邮件
def update(self, instance, validated_data):
        email = validated_data['email']
        instance.email = email
        instance.save()

        # 生成验证链接
        verify_url = instance.generate_verify_email_url()
        # 发送验证邮件
        send_verify_email.delay(email, verify_url)
        return instance
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
前端

修改user_center_info.js文件,增加save_email方法

// 保存email
        save_email: function(){
            var re = /^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$/;
            if(re.test(this.email)) {
                this.email_error = false;
            } else {
                this.email_error = true;
                return;
            }
            axios.put(this.host + '/email/',
                { email: this.email },
                {
                    headers: {
                        'Authorization': 'JWT ' + this.token
                    },
                    responseType: 'json'
                })
                .then(response => {
                    this.set_email = false;
                    this.send_email_btn_disabled = true;
                    this.send_email_tip = '已发送验证邮件'
                })
                .catch(error => {
                    alert(error.data);
                });
        }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

验证邮箱链接

当用户点击邮箱里的链接时,进入到success_verify_email.html页面。

在该页面中,我们将请求网址中用于验证的token发送给后端接口,由后端接口判断token的有效性,如果token有效,则修改邮箱的验证状态,并将处理结果返回给前端展示给用户。

后端接口设计:

请求方式:GET /emails/verification/?token=xxx

请求参数: 查询字符串参数

参数类型是否必须说明
tokenstr用于验证邮箱的token

返回数据: JSON

返回值类型是否必须说明
messagestr验证处理结果

在users/views.py 中新建视图

  
  
# url(r'^emails/verification/$', views.VerifyEmailView.as_view()),
  
  
class VerifyEmailView(APIView):
    """
    邮箱验证
    """
    def get(self, request):
        # 获取token
        token = request.query_params.get('token')
        if not token:
            return Response({'message': '缺少token'}, status=status.HTTP_400_BAD_REQUEST)

        # 验证token
        user = User.check_verify_email_token(token)
        if user is None:
            return Response({'message': '链接信息无效'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            user.email_active = True
            user.save()
            return Response({'message': 'OK'})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

在User模型类中定义验证token的方法

@staticmethod
    def check_verify_email_token(token):
        """
        检查验证邮件的token
        """
        serializer = TJWSSerializer(settings.SECRET_KEY, expires_in=constants.VERIFY_EMAIL_TOKEN_EXPIRES)
        try:
            data = serializer.loads(token)
        except BadData:
            return None
        else:
            email = data.get('email')
            user_id = data.get('user_id')
            try:
                user = User.objects.get(id=user_id, email=email)
            except User.DoesNotExist:
                return None
            else:
                return user
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
前端

在front_end_pc目录中新建success_verify_email.html文件

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
<html xmlns=" 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/vue-2.5.16.js"></script>
    <script type="text/javascript" src="js/axios-0.18.0.min.js"></script>
    <script type="text/javascript" src="js/host.js"></script>
</head>
<body>
    <div class="find_header">
        <img src="images/logo.png">
        <span class="sub_page_name fl">|&nbsp;&nbsp;&nbsp;&nbsp;邮箱验证成功</span>
    </div>

    <div class="find_form" id="email_result">
        <div v-if="success" class="pass_change_finish">恭喜您,邮箱验证成功!<br/><a rel="nofollow" href="/index.html">返回主页</a></div>
        <div v-else class="pass_change_finish">链接已失效,验证失败,请重新验证!<br/><a rel="nofollow" href="/index.html">返回主页</a></div>
    </div>

    <div class="footer no-mp">
        <div class="foot_link">
            <a rel="nofollow" href="#">关于我们</a>
            <span>|</span>
            <a rel="nofollow" href="#">联系我们</a>
            <span>|</span>
            <a rel="nofollow" href="#">招聘人才</a>
            <span>|</span>
            <a rel="nofollow" href="#">友情链接</a>        
        </div>
        <p>CopyRight © 2016 北京美多商业股份有限公司 All Rights Reserved</p>
        <p>电话:010-****888    京ICP备*******8号</p>
    </div>
    <script>
        var vm = new Vue({
            el: '#email_result',
            data: {
                host,
                success: true
            },
            created: function(){
                axios.get(this.host+'/emails/verification/'+ document.location.search)
                    .then(response => {
                        this.success = true;
                    })
                    .catch(error => {
                        this.success = false;
                    });
            }
        });
    </script>    
</body>
</html>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

收货地址

【Django开发】django美多商城项目完整开发4.0第9篇:邮件与验证,学习目标:【附代码文档】_django_09

在这个页面中,我们要实现用户地址的管理,主要的业务逻辑有:

  • 省市区地址的数据库建立与查询
  • 用户地址的增删改查处理
  • 设置默认地址
  • 设置地址标题

我们主要讲解省市区地址的三级联动和缓存,其余留给大家自己来实现。

学习目标:

  • 省市区三级联动
  • 在Django REST framework中使用缓存