Django商城项目-注册页面图片验证码接口实现

一、用户部分(图片验证码接口实现)

一、用户模型类

1.创建自定义的模型类

Django认证系统中提供的用户模型类及方法很方便,我们可以使用这个模型类,但是字段有些无法满足项目需求,如本项目中需要保存用户的手机号,需要给模型类添加额外的字段。

我们需要继承抽象模型类django.contrib.auth.models.AbstractUser,并新增手机号码字段。

(1).在meiduo_mall/meiduo_mall/apps中创建Django应用users,并在配置文件中注册users应用

# 1.终端进入虚拟环境--进入apps路径操作
python manage.py startapp users

# 2.pycharm中工程下dev.py中注册应用
INSTALLED_APPS = [
   ...
    'users.apps.UsersConfig',
]

(2).在users应用下的models.py中定义用户的模型类

from django.contrib.auth.models import AbstractUser
from django.db import models


# Create your models here.
class User(AbstractUser):
    """用户模型类"""
    mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')

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

(3).在配置文件dev.py中告知Django认证系统使用我们自定义的模型类。

AUTH_USER_MODEL = 'users.User'

AUTH_USER_MODEL 参数的设置以点.来分隔,表示应用名.模型类名,且只能出现一个.,所以前边才需要设置导包路径。

注意:Django建议我们对于AUTH_USER_MODEL参数的设置一定要在第一次数据库迁移之前就设置好,否则后续使用可能出现未知错误,如设置密码但登陆出错。

(4).终端中manage.py同级目录执行数据库迁移命令

python manage.py makemigrations
python manage.py migrate

二、注册业务接口

1.注册业务接口分析

在用户注册中,需要实现以下接口:

  • 图片验证码
  • 短信验证码
  • 用户名判断是否存在
  • 手机号判断是否存在
  • 注册保存用户数据

图片验证码、短信验证码考虑到后续可能会在其他业务中也用到,因此我们将图片验证码独立。

在meiduo_mall/meiduo_mall/apps创建一个新应用verifications并注册到配置文件dev.py,在此应用中实现图片验证码、短信验证码

# 1.在指定虚拟环境下,进入apps路径,创建子应用
python manage.py startapp verifications

# 2.在dev.py注册子应用verifications 
INSTALLED_APPS = [
   ...
   'verifications.apps.VerificationsConfig',
]

2.图片验证码

(1)后端接口设计

访问方式:

GET /image_codes/(?P\w{8}(-\w{4}){3}-\w{12})/

请求参数: 路径参数

参数 类型 是否必须 说明
image_code_id uuid字符串 图片验证码编号

返回数据:验证码图片

(2) 具体后端视图实现

1.将图片验证码第三方库文件captcha复制到libs目录下。

2.在verifications/views.py中,

import logging
from django.http import HttpResponse

from rest_framework import status
from rest_framework.views import APIView
from meiduo_mall.libs.captcha.captcha import captcha
from django_redis import get_redis_connection

from . import constants

# 获取在配置文件中定义的logger日志器,用来记录日志
logger = logging.getLogger('django')


#  GET /image_codes/(?P[\w-]+)/
class ImageCodeView(APIView):
    """图片验证码"""
    def get(self, request, image_code_id):
        # 接收参数(路径参数,直接接收)
        # 校验参数(路由正则表达式验证uuid格式,无需序列化器进行验证)
        # 1.生成图片验证码
        text, image = captcha.generate_captcha()

        # 2.获取redis连接对象,并保存图片验证码真实结果
        redis_conn = get_redis_connection('verify_codes')
        redis_conn.setex("img_%s" % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)

        # 3.固定返回验证码图片数据,不需要REST framework框架的Response帮助我们决定返回响应数据的格式
        # 所以此处直接使用Django原生的HttpResponse即可
        # return HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
        return HttpResponse(content=image, content_type='image/jpg', status=200)

        # 浏览器测试示例:http://api.meiduo.site:8000/image_codes/ed96d5ca-c88e-4a64-b1fb-52ccd8810f00/


如果想使用DRF的Response,需要自己设置图片渲染器,并添加到配置文件当中:

# utils/renders.py
from rest_framework import renderers
class JPEGRenderer(renderers.BaseRenderer):
    media_type = 'image/jpeg'
    format = 'jpg'
    charset = None
    render_style = 'binary'

    def render(self, data, media_type=None, renderer_context=None):
        return data

此时,可以更改图片验证码的代码:

from meiduo_mall.utils.renders import JPEGRenderer
class ImageCodeView(APIView):
    renderer_classes = [JPEGRenderer]
    def get(self, request: Request, image_code_id):
        """
        :param request:
        :param image_code_id: 图片id
        :return:
        """
        
        # 调用第三方包生成图片验证码
        text, image = captcha.generate_captcha()
        # 将图片验证码保存到reids数据库
        redis_conn = get_redis_connection('verify_codes')
        redis_conn.setex('img_%s' % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
       
        return Response(image, content_type='image/jpeg')

3.django-redis提供了get_redis_connection的方法,通过调用get_redis_connection方法传递redis的配置名称可获取到redis的连接对象,通过redis连接对象可以执行redis命令。

我们需要在配置文件中添加一个新的redis配置,用于存放验证码数据

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://10.211.55.5:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "session": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://10.211.55.5:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "verify_codes": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://10.211.55.5:6379/2",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

4.在verifications中新增constants.py文件,添加常量。

# 图片验证码redis中存放的有效期,单位秒(默认5分钟)
IMAGE_CODE_REDIS_EXPIRES = 5 * 60

# 短信验证码在redis的有效期,单位秒
SMS_CODE_REDIS_EXPIRES = 5 *60

5.在verifications子应用中新增url.py文件,在其中注册图片验证码视图路由

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^image_codes/(?P\w{8}(-\w{4}){3}-\w{12})/$', views.ImageCodeView.as_view()),
]

6.在meiduo_mall/urls.py中注册子应用verifications路由

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'', include('verifications.urls')),
]

3. 设置域名

我们现在为前端和后端分别设置两个不同的域名

位置 域名
前端 www.meiduo.site
后端 api.meiduo.site

编辑/etc/hosts文件,可以设置本地域名

sudo vim /etc/hosts

在文件中增加两条信息(i 进入编辑模式,esc退出编辑模式,:wq 保存退出)

127.0.0.1   api.meiduo.site
127.0.0.1   www.meiduo.site

windows系统中若设置本地域名,hosts文件在如下目录:

C:\Windows\System32\drivers\etc

我们在前端front_end_pc/js目录中,创建host.js文件用以为前端保存后端域名

var host = 'http://api.meiduo.site:8000';

在所有需要访问后端接口的前端页面中都引入host.js,使用host变量即可指代后端域名。

修改settings/dev.py配置中的ALLOWED_HOSTS

一旦不再使用127.0.0.1访问Django后端,需要在配置文件中修改ALLOWED_HOSTS,增加可以访问后端的域名

ALLOWED_HOSTS = [
    'api.meiduo.site', 
    '127.0.0.1',
    'localhost', 
    'www.meiduo.site'
]

4. 前端Vue代码:

js/register.js

data: {
    ...
    image_code_id: '',  // 图片验证码编号
    image_code_url: '',  // 验证码图片路径
},
mounted: function() {
    this.generate_image_code();
},
methods: {
    // 生成uuid
    generate_uuid: function(){
        var d = new Date().getTime();
        if(window.performance && typeof window.performance.now === "function"){
            d += performance.now(); //use high-precision timer if available
        }
        var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = (d + Math.random()*16)%16 | 0;
            d = Math.floor(d/16);
            return (c =='x' ? r : (r&0x3|0x8)).toString(16);
        });
        return uuid;
    },
    // 生成一个图片验证码的编号,并设置页面中图片验证码img标签的src属性
    generate_image_code: function(){
        // 生成一个编号
        // 严格一点的使用uuid保证编号唯一, 不是很严谨的情况下,也可以使用时间戳
        this.image_code_id = this.generate_uuid();

        // 设置页面中图片验证码img标签的src属性
        this.image_code_url = this.host + "/image_codes/" + this.image_code_id + "/";
    },
    ...
}

你可能感兴趣的:(Django框架)