【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_python

本教程的知识点为: 项目准备 项目准备 配置 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美多商城项目/note.md

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

全套教程部分目录:

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_02

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_框架_03


部分文件图片:

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_后端_04

用户基本信息

查询并渲染用户基本信息

1. 用户模型补充email_active字段
  • 由于在渲染用户基本信息时,需要渲染用户邮箱验证的状态,所以需要给用户模型补充email_active字段

  • 补充完字段后,需要进行迁移。

$ python manage.py makemigrations
  $ python manage.py migrate
  • 1.
  • 2.
class User(AbstractUser):
    """自定义用户模型类"""
    mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')
    email_active = models.BooleanField(default=False, verbose_name='邮箱验证状态')

    class Meta:
        db_table = 'tb_users'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
2. 查询用户基本信息
class UserInfoView(LoginRequiredMixin, View):
    """用户中心"""

    def get(self, request):
        """提供个人信息界面"""
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }
        return render(request, 'user_center_info.html', context=context)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
3. 渲染用户基本信息

1.将后端模板数据传递到Vue.js

  • 为了方便实现用户添加邮箱时的界面局部刷新

    • 我们将后端提供的用户数据传入到user_center_info.js
<script type="text/javascript">
    let username = "{{ username }}";
    let mobile = "{{ mobile }}";
    let email = "{{ email }}";
    let email_active = "{{ email_active }}";
</script>
<script type="text/javascript" src="{{ static('js/common.js') }}"></script>
<script type="text/javascript" src="{{ static('js/user_center_info.js') }}"></script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
data: {
    username: username,
    mobile: mobile,
    email: email,
    email_active: email_active,
},
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

2.Vue渲染用户基本信息:user_center_info.html

<div class="info_con clearfix" v-cloak>
    <h3 id="h0" 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" @blur="check_email" type="email" name="email" class="email">
                <input @click="save_email" type="button" name="" value="保 存">
                <input @click="cancel_email" type="reset" name="" value="取 消">
                <div v-show="error_email" class="error_email_tip">邮箱格式错误</div>
            </div>
            <div v-else>
                <input v-model="email" type="email" name="email" class="email" readonly>
                <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>
        </li>
    </ul>
</div>
  • 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.

添加和验证邮箱

添加邮箱后端逻辑

1. 添加邮箱接口设计和定义

1.请求方式

选项方案
请求方法PUT
请求地址/emails/

2.请求参数

参数名类型是否必传说明
emailstring邮箱

3.响应结果:JSON

字段说明
code状态码
errmsg错误信息
2. 添加邮箱后端逻辑实现
class EmailView(View):
    """添加邮箱"""

    def put(self, request):
        """实现添加邮箱逻辑"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        email = json_dict.get('email')

        # 校验参数
        if not email:
            return http.HttpResponseForbidden('缺少email参数')
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.HttpResponseForbidden('参数email有误')

        # 赋值email字段
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '添加邮箱失败'})

        # 响应添加邮箱结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加邮箱成功'})
  • 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.
3. 判断用户是否登录并返回JSON

重要提示:

  • 只有用户登录时才能让其绑定邮箱。
  • 此时前后端交互的数据类型是JSON,所以需要判断用户是否登录并返回JSON给用户。

方案一:

  • 使用Django用户认证系统提供的is_authenticated()
class EmailView(View):
    """添加邮箱"""

    def put(self, request):
        """实现添加邮箱逻辑"""
        # 判断用户是否登录并返回JSON
        if not request.user.is_authenticated():
            return http.JsonResponse({'code': RETCODE.SESSIONERR, 'errmsg': '用户未登录'})
        pass
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

方案二:

  • 自定义返回JSON的login_required装饰器
  • meiduo_mall.utils.views.py
def login_required_json(view_func):
    """
    判断用户是否登录的装饰器,并返回json
    :param view_func: 被装饰的视图函数
    :return: json、view_func
    """
    # 恢复view_func的名字和文档
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):

        # 如果用户未登录,返回json数据
        if not request.user.is_authenticated():
            return http.JsonResponse({'code': RETCODE.SESSIONERR, 'errmsg': '用户未登录'})
        else:
            # 如果用户登录,进入到view_func中
            return view_func(request, *args, **kwargs)

    return wrapper


class LoginRequiredJSONMixin(object):
    """验证用户是否登陆并返回json的扩展类"""
    @classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        return login_required_json(view)
  • 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.

LoginRequiredJSONMixin的使用

class EmailView(LoginRequiredJSONMixin, View):
    """添加邮箱"""

    def put(self, request):
        """实现添加邮箱逻辑"""
        # 判断用户是否登录并返回JSON
        pass
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

Django发送邮件的配置

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_python_05

1. Django发送邮件流程分析

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_python_06

send_mall()方法介绍

  • 位置:

    • django.core.mail模块提供了send_mail()来发送邮件。
  • 方法参数:

    • send_mail(subject, message, from_email, recipient_list, html_message=None)
subject 邮件标题
message 普通邮件正文,普通字符串
from_email 发件人
recipient_list 收件人列表
html_message 多媒体邮件正文,可以是html字符串
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
2. 准备发邮件服务器

1.点击进入《设置》界面

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_07

2.点击进入《客户端授权密码》界面

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_08

3.开启《授权码》,并完成验证短信

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_09

4.填写《授权码》

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_10

5.完成《授权码》设置

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_11

6.配置邮件服务器

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 指定邮件后端
EMAIL_HOST = 'smtp.163.com' # 发邮件主机
EMAIL_PORT = 25 # 发邮件端口
EMAIL_HOST_USER = 'hmmeiduo@163.com' # 授权的邮箱
EMAIL_HOST_PASSWORD = 'hmmeiduo123' # 邮箱授权时获得的密码,非注册登录密码
EMAIL_FROM = '美多商城<hmmeiduo@163.com>' # 发件人抬头
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

发送邮箱验证邮件

重要提示:

  • 发送邮箱验证邮件是耗时的操作,不能阻塞美多商城的响应,所以需要异步发送邮件
  • 我们继续使用Celery实现异步任务。
1. 定义和调用发送邮件异步任务

1.定义发送邮件任务

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_python_12

@celery_app.task(bind=True, name='send_verify_email', retry_backoff=3)
def send_verify_email(self, 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)
    try:
        send_mail(subject, "", settings.EMAIL_FROM, [to_email], html_message=html_message)
    except Exception as e:
        logger.error(e)
        # 有异常自动重试三次
        raise self.retry(exc=e, max_retries=3)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

2.注册发邮件的任务:main.py

  • 在发送邮件的异步任务中,我们用到了Django的配置文件。
  • 所以我们需要修改celery的启动文件main.py。
  • 在其中指明celery可以读取的Django配置文件。
  • 最后记得注册新添加的email的任务
  
  
# celery启动文件
  
  
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.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

3.调用发送邮件异步任务

  
  
# 赋值email字段
  
  
try:
    request.user.email = email
    request.user.save()
except Exception as e:
    logger.error(e)
    return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '添加邮箱失败'})

  
  
# 异步发送验证邮件
  
  
verify_url = '邮件验证链接'
send_verify_email.delay(email, verify_url)

  
  
# 响应添加邮箱结果
  
  
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加邮箱成功'})
  • 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.

4.启动Celery

$ celery -A celery_tasks.main worker -l info
  • 1.
2. 生成邮箱验证链接

1.定义生成邮箱验证链接方法

def generate_verify_email_url(user):
    """
    生成邮箱验证链接
    :param user: 当前登录用户
    :return: verify_url
    """
    serializer = Serializer(settings.SECRET_KEY, expires_in=constants.VERIFY_EMAIL_TOKEN_EXPIRES)
    data = {'user_id': user.id, 'email': user.email}
    token = serializer.dumps(data).decode()
    verify_url = settings.EMAIL_VERIFY_URL + '?token=' + token
    return verify_url
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

2.配置相关参数

  
  
# 邮箱验证链接
  
  
EMAIL_VERIFY_URL = '
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

3.使用邮箱验证链接

verify_url = generate_verify_email_url(request.user)
send_verify_email.delay(email, verify_url)
  • 1.
  • 2.

验证邮箱后端逻辑

1. 验证邮箱接口设计和定义

1.请求方式

选项方案
请求方法GET
请求地址/emails/verification/

2.请求参数:查询参数

参数名类型是否必传说明
tokenstring邮箱激活链接

3.响应结果:HTML

字段说明
邮箱验证失败响应错误提示
邮箱验证成功重定向到用户中心
2. 验证链接提取用户信息
def check_verify_email_token(token):
    """
    验证token并提取user
    :param token: 用户信息签名后的结果
    :return: user, None
    """
    serializer = Serializer(settings.SECRET_KEY, expires_in=constants.VERIFY_EMAIL_TOKEN_EXPIRES)
    try:
        data = serializer.loads(token)
    except BadData:
        return None
    else:
        user_id = data.get('user_id')
        email = data.get('email')
        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.
  • 20.
3. 验证邮箱后端逻辑实现

验证邮箱的核心:就是将用户的email_active字段设置为True

class VerifyEmailView(View):
    """验证邮箱"""

    def get(self, request):
        """实现邮箱验证逻辑"""
        # 接收参数
        token = request.GET.get('token')

        # 校验参数:判断token是否为空和过期,提取user
        if not token:
            return http.HttpResponseBadRequest('缺少token')

        user = check_verify_email_token(token)
        if not user:
            return http.HttpResponseForbidden('无效的token')

        # 修改email_active的值为True
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return http.HttpResponseServerError('激活邮件失败')

        # 返回邮箱验证结果
        return redirect(reverse('users:info'))
  • 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.

收货地址

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_13

【Django开发】前后端分离django美多商城项目第8篇:用户基本信息,1. 用户模型补充email_active字段【附代_django_14

用户地址的主要业务逻辑有:

  1. 展示省市区数据
  2. 用户地址的增删改查处理
  3. 设置默认地址
  4. 设置地址标题