本教程的知识点为: 项目准备 项目准备 配置 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客户端 安装 使用。

移步这里全套资料代码:

博客文章1:  https://blog.51cto.com/u_16958431/11766349

博客文章2:  https://blog.51cto.com/u_16958431/11744136

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

全套教程部分目录:


部分文件图片:

用户注册业务实现

用户注册前端逻辑

为了学会使用Vue.js的双向绑定实现用户的交互和页面局部刷新效果。

1. 用户注册页面绑定Vue数据

1.准备div盒子标签

<div id="app">
    <body>
    ......
    </body>
</div>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

2.register.html

  • 绑定内容:变量、事件、错误提示等
<form method="post" class="register_form" @submit="on_submit" v-cloak>
    {{ csrf_input }}
    <ul>
        <li>
            <label>用户名:</label>
            <input type="text" v-model="username" @blur="check_username" name="username" id="user_name">
            <span class="error_tip" v-show="error_name">[[ error_name_message ]]</span>
        </li>
        <li>
            <label>密码:</label>
            <input type="password" v-model="password" @blur="check_password" name="password" id="pwd">
            <span class="error_tip" v-show="error_password">请输入8-20位的密码</span>
        </li>
        <li>
            <label>确认密码:</label>
            <input type="password" v-model="password2" @blur="check_password2" name="password2" id="cpwd">
            <span class="error_tip" v-show="error_password2">两次输入的密码不一致</span>
        </li>
        <li>
            <label>手机号:</label>
            <input type="text" v-model="mobile" @blur="check_mobile" name="mobile" id="phone">
            <span class="error_tip" v-show="error_mobile">[[ error_mobile_message ]]</span>
        </li>
        <li>
            <label>图形验证码:</label>
            <input type="text" name="image_code" id="pic_code" class="msg_input">
            <img src="{{ static('images/pic_code.jpg') }}" alt="图形验证码" class="pic_code">
            <span class="error_tip">请填写图形验证码</span>
        </li>
        <li>
            <label>短信验证码:</label>
            <input type="text" name="sms_code" id="msg_code" class="msg_input">
            <a rel="nofollow" href="javascript:;" class="get_msg_code">获取短信验证码</a>
            <span class="error_tip">请填写短信验证码</span>
        </li>
        <li class="agreement">
            <input type="checkbox" v-model="allow" @change="check_allow" name="allow" id="allow">
            <label>同意”美多商城用户使用协议“</label>
            <span class="error_tip2" v-show="error_allow">请勾选用户协议</span>
        </li>
        <li class="reg_sub">
            <input type="submit" value="注 册">
        </li>
    </ul>
</form>
  • 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.
2. 用户注册JS文件实现用户交互

1.导入Vue.js库和ajax请求的库

<script type="text/javascript" src="{{ static('js/vue-2.5.16.js') }}"></script>
<script type="text/javascript" src="{{ static('js/axios-0.18.0.min.js') }}"></script>
  • 1.
  • 2.

2.准备register.js文件

<script type="text/javascript" src="{{ static('js/register.js') }}"></script>
  • 1.

绑定内容:变量、事件、错误提示等

let vm = new Vue({
    el: '#app',
    // 修改Vue读取变量的语法
    delimiters: ['[[', ']]'],
    data: {
        username: '',
        password: '',
        password2: '',
        mobile: '',
        allow: '',

        error_name: false,
        error_password: false,
        error_password2: false,
        error_mobile: false,
        error_allow: false,

        error_name_message: '',
        error_mobile_message: '',
    },
    methods: {
        // 校验用户名
        check_username(){
        },
        // 校验密码
        check_password(){
        },
        // 校验确认密码
        check_password2(){
        },
        // 校验手机号
        check_mobile(){
        },
        // 校验是否勾选协议
        check_allow(){
        },
        // 监听表单提交事件
        on_submit(){
        },
    }
});
  • 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.

3.用户交互事件实现

methods: {
    // 校验用户名
    check_username(){
        let re = /^[a-zA-Z0-9_-]{5,20}$/;
        if (re.test(this.username)) {
            this.error_name = false;
        } else {
            this.error_name_message = '请输入5-20个字符的用户名';
            this.error_name = true;
        }
    },
    // 校验密码
    check_password(){
        let re = /^[0-9A-Za-z]{8,20}$/;
        if (re.test(this.password)) {
            this.error_password = false;
        } else {
            this.error_password = true;
        }
    },
    // 校验确认密码
    check_password2(){
        if(this.password != this.password2) {
            this.error_password2 = true;
        } else {
            this.error_password2 = false;
        }
    },
    // 校验手机号
    check_mobile(){
        let re = /^1[3-9]\d{9}$/;
        if(re.test(this.mobile)) {
            this.error_mobile = false;
        } else {
            this.error_mobile_message = '您输入的手机号格式不正确';
            this.error_mobile = true;
        }
    },
    // 校验是否勾选协议
    check_allow(){
        if(!this.allow) {
            this.error_allow = true;
        } else {
            this.error_allow = false;
        }
    },
    // 监听表单提交事件
    on_submit(){
        this.check_username();
        this.check_password();
        this.check_password2();
        this.check_mobile();
        this.check_allow();

        if(this.error_name == true || this.error_password == true || this.error_password2 == true
            || this.error_mobile == true || this.error_allow == true) {
            // 禁用表单的提交
            window.event.returnValue = false;
        }
    },
}
  • 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.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
4. 知识要点
  1. Vue绑定页面的套路

    • 导入Vue.js库和ajax请求的库
    • 准备div盒子标签
    • 准备js文件
    • html页面绑定变量、事件等
    • js文件定义变量、事件等
  2. 错误提示

    • 如果错误提示信息是固定的,可以把错误提示信息写死,再通过v-show控制是否展示
    • 如果错误提示信息不是固定的,可以使用绑定的变量动态的展示错误提示信息,再通过v-show控制是否展示
  3. 修改Vue变量的读取语法,避免和Django模板语法冲突

    • delimiters: ['[[', ']]']
  4. 后续的页面中如果有类似的交互和刷新效果,也可按照此套路实现

用户注册后端逻辑

1. 接收参数

提示:用户注册数据是从注册表单发送过来的,所以使用**request.POST**来提取。

username = request.POST.get('username')
password = request.POST.get('password')
password2 = request.POST.get('password2')
mobile = request.POST.get('mobile')
allow = request.POST.get('allow')
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
2. 校验参数

前端校验过的后端也要校验,后端的校验和前端的校验是一致的

  
  
# 判断参数是否齐全
  
  
  
  
# 判断用户名是否是5-20个字符
  
  
  
  
# 判断密码是否是8-20个数字
  
  
  
  
# 判断两次密码是否一致
  
  
  
  
# 判断手机号是否合法
  
  
  
  
# 判断是否勾选用户协议
  
  
  • 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.
  
  
# 判断参数是否齐全
  
  
if not all([username, password, password2, mobile, allow]):
    return http.HttpResponseForbidden('缺少必传参数')
  
  
# 判断用户名是否是5-20个字符
  
  
if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
    return http.HttpResponseForbidden('请输入5-20个字符的用户名')
  
  
# 判断密码是否是8-20个数字
  
  
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
    return http.HttpResponseForbidden('请输入8-20位的密码')
  
  
# 判断两次密码是否一致
  
  
if password != password2:
    return http.HttpResponseForbidden('两次输入的密码不一致')
  
  
# 判断手机号是否合法
  
  
if not re.match(r'^1[3-9]\d{9}$', mobile):
    return http.HttpResponseForbidden('请输入正确的手机号码')
  
  
# 判断是否勾选用户协议
  
  
if allow != 'on':
    return http.HttpResponseForbidden('请勾选用户协议')
  • 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.

提示:这里校验的参数,前端已经校验过,如果此时参数还是出错,说明该请求是非正常渠道发送的,所以直接禁止本次请求。

3. 保存注册数据
  • 这里使用Django认证系统用户模型类提供的 create_user() 方法创建新的用户。
  • 这里 create_user() 方法中封装了 set_password() 方法加密密码。
  
  
# 保存注册数据
  
  
try:
    User.objects.create_user(username=username, password=password, mobile=mobile)
except DatabaseError:
    return render(request, 'register.html', {'register_errmsg': '注册失败'})

  
  
# 响应注册结果
  
  
return http.HttpResponse('注册成功,重定向到首页')
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

如果注册失败,我们需要在页面上渲染出注册失败的提示信息。

{% if register_errmsg %}
    <span class="error_tip2">{{ register_errmsg }}</span>
{% endif %}
  • 1.
  • 2.
  • 3.
4. 响应注册结果
  • 重要提示:注册成功,重定向到首页

1.创建首页广告应用:contents

$ cd ~/projects/meiduo_project/meiduo_mall/meiduo_mall/apps
$ python ../../manage.py startapp contents
  • 1.
  • 2.

2.定义首页广告视图:IndexView

class IndexView(View):
    """首页广告"""

    def get(self, request):
        """提供首页广告界面"""
        return render(request, 'index.html')
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

3.配置首页广告路由:绑定命名空间

  
  
# contents
  
  
url(r'^', include('contents.urls', namespace='contents')),
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  
  
# 首页广告
  
  
url(r'^$', views.IndexView.as_view(), name='index'),
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

4.测试首页广告是否可以正常访问


  • 1.

5.响应注册结果:重定向到首页

  
  
# 响应注册结果
  
  
return redirect(reverse('contents:index'))
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
5. 知识要点
  1. 后端逻辑编写套路:

    • 业务逻辑分析
    • 接口设计和定义
    • 接收和校验参数
    • 实现主体业务逻辑
    • 响应结果
  2. 注册业务逻辑核心思想:

    • 保存用户注册数据

状态保持

说明:

  • 如果需求是注册成功后即表示用户登入成功,那么此时可以在注册成功后实现状态保持
  • 如果需求是注册成功后不表示用户登入成功,那么此时不用在注册成功后实现状态保持

美多商城的需求是:注册成功后即表示用户登入成功

1. login()方法介绍
  1. 用户登入本质:

    • 状态保持
    • 将通过认证的用户的唯一标识信息(比如:用户ID)写入到当前浏览器的 cookie 和服务端的 session 中。
  2. login()方法:

    • Django用户认证系统提供了login()方法。
    • 封装了写入session的操作,帮助我们快速登入一个用户,并实现状态保持。
  3. login()位置:

    • django.contrib.auth.__init__.py文件中。

    login(request, user, backend=None)

    
    
    
    
    4. 状态保持 session 数据存储的位置:**Redis数据库的1号库**
    ```python
    SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    SESSION_CACHE_ALIAS = "session"
    
    • 1.
    • 2.
    • 3.
    • 4.
    • 5.
    • 6.
    • 7.
    • 8.
    2. login()方法登入用户
      
      
    # 保存注册数据
      
      
    try:
        user = User.objects.create_user(username=username, password=password, mobile=mobile)
    except DatabaseError:
        return render(request, 'register.html', {'register_errmsg': '注册失败'})
    
      
      
    # 登入用户,实现状态保持
      
      
    login(request, user)
    
      
      
    # 响应注册结果
      
      
    return redirect(reverse('contents:index'))
    
    • 1.
    • 2.
    • 3.
    • 4.
    • 5.
    • 6.
    • 7.
    • 8.
    • 9.
    • 10.
    • 11.
    • 12.
    • 13.
    • 14.
    • 15.
    • 16.
    • 17.
    • 18.
    • 19.
    • 20.
    • 21.
    • 22.
    • 23.
    3. 查看状态保持结果

    4. 知识要点
    1. 登入用户,并实现状态保持的方式:login(request, user, backend=None)

    用户名重复注册

    1. 用户名重复注册逻辑分析

    2. 用户名重复注册接口设计和定义

    1.请求方式

    选项方案
    请求方法GET
    请求地址/usernames/(?P[a-zA-Z0-9_-]{5,20})/count/

    2.请求参数:路径参数

    参数名类型是否必传说明
    usernamestring用户名

    3.响应结果:JSON

    响应结果响应内容
    code状态码
    errmsg错误信息
    count记录该用户名的个数
    3. 用户名重复注册后端逻辑
    class UsernameCountView(View):
        """判断用户名是否重复注册"""
    
        def get(self, request, username):
            """
            :param request: 请求对象
            :param username: 用户名
            :return: JSON
            """
            count = User.objects.filter(username=username).count()
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})
    
    • 1.
    • 2.
    • 3.
    • 4.
    • 5.
    • 6.
    • 7.
    • 8.
    • 9.
    • 10.
    • 11.
    4. 用户名重复注册前端逻辑
    if (this.error_name == false) {
        let url = '/usernames/' + this.username + '/count/';
        axios.get(url,{
            responseType: 'json'
        })
            .then(response => {
                if (response.data.count == 1) {
                    this.error_name_message = '用户名已存在';
                    this.error_name = true;
                } else {
                    this.error_name = false;
                }
            })
            .catch(error => {
                console.log(error.response);
            })
    }
    
    • 1.
    • 2.
    • 3.
    • 4.
    • 5.
    • 6.
    • 7.
    • 8.
    • 9.
    • 10.
    • 11.
    • 12.
    • 13.
    • 14.
    • 15.
    • 16.
    • 17.
    5. 知识要点
    1. 判断用户名重复注册的核心思想:

      • 使用用户名查询该用户名对应的记录是否存在,如果存在,表示重复注册了,反之,没有重复注册。
    2. axios发送异步请求套路:

      • 处理用户交互
      • 收集请求参数
      • 准备请求地址
      • 发送异步请求
      • 得到服务器响应
      • 控制界面展示效果

    手机号重复注册

    1. 手机号重复注册逻辑分析

    2. 手机号重复注册接口设计和定义

    1.请求方式

    选项方案
    请求方法GET
    请求地址/mobiles/(?P1[3-9]\d{9})/count/

    2.请求参数:路径参数

    参数名类型是否必传说明
    mobilestring手机号

    3.响应结果:JSON

    响应结果响应内容
    code状态码
    errmsg错误信息
    count记录该用户名的个数
    3. 手机号重复注册后端逻辑
    class MobileCountView(View):
        """判断手机号是否重复注册"""
    
        def get(self, request, mobile):
            """
            :param request: 请求对象
            :param mobile: 手机号
            :return: JSON
            """
            count = User.objects.filter(mobile=mobile).count()
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})
    
    • 1.
    • 2.
    • 3.
    • 4.
    • 5.
    • 6.
    • 7.
    • 8.
    • 9.
    • 10.
    • 11.
    4. 手机号重复注册前端逻辑
    if (this.error_mobile == false) {
        let url = '/mobiles/'+ this.mobile + '/count/';
        axios.get(url, {
            responseType: 'json'
        })
            .then(response => {
                if (response.data.count == 1) {
                    this.error_mobile_message = '手机号已存在';
                    this.error_mobile = true;
                } else {
                    this.error_mobile = false;
                }
            })
            .catch(error => {
                console.log(error.response);
            })
    }
    
    • 1.
    • 2.
    • 3.
    • 4.
    • 5.
    • 6.
    • 7.
    • 8.
    • 9.
    • 10.
    • 11.
    • 12.
    • 13.
    • 14.
    • 15.
    • 16.
    • 17.

    验证码

    图形验证码

    图形验证码逻辑分析

    需要新建应用verifications

    知识要点
    1. 将图形验证码的文字信息保存到Redis数据库,为短信验证码做准备。
    2. UUID 用于唯一区分该图形验证码属于哪个用户,也可使用其他唯一标识信息来实现。