数据库创建一条数据

django操作数据库模型

一、用户模型设计(用户表的设计)

  1.用户表字段分析

    用户名(账户名)

    密码

    手机

    邮箱

    邮箱是否有效

  2.用户模型设计

    开发效率极高,内置了很多功能,权限验证,

    自定义User模型

    项目app/user/models.py

    创建

class UserManager(_UserManager):
    """
    自定义管理器,用来修改使用create_superuser命令创建用户必须使用email的行为
    """
    def create_superuser(self, username, password, email=None, **extra_fields):
        super().create_superuser(username=username, password=password,
                                 email=email, **extra_fields)
class User(AbstractUser):
    """
    自定义的User模型,添加moblie, email_active字段
    """
    mobile = models.CharField('手机号', max_length=11, unique=True,
                              help_text='手机号', error_messages={'unique': '此手机号码已注册'})

    email_active = models.BooleanField('邮箱状态', default=False)

    class Meta:
        db_table = 'tb_user'  # 指定数据库表名
        verbose_name = '用户'  # 在admin站点中的显示名称
        verbose_name_plural = verbose_name  # 复数

    def __str__(self):
        return self.username

    # 通过create_superuser 这个命令创建用户时,需要的字段
    REQUIRED_FIELDS = ['mobile']

    # user模型相当强大,还有修改必须输入email的这个行为
    # 管理器执行
    objects = UserManager()

操作数据库: 项目根目录输入命令:python manage.py makemigrations
      创建数据库表格:python manage.py migrate
      进入数据库:mysql -uroot -pqwe123
创建超级管理员:python manage.py createsuperuser

二、功能模型设计

1.接口设计思路

  1-1分析业务逻辑,明确在这个业务中需要涉及到几个相关业务,将每个子业务当做一个接口设计

  1-2分析接口的功能,明确接口的访问方式和返回数据

    1-2-1接口的请求方式:GET PUT POST DELTE

    1-2-2接口的URL定义

    1-2-3需要接受什么参数(路径参数、查询参数、表单,json)

    1-2-4返回的数据,及数据格式

2.注册功能分析

  1,流程

  2,功能接口

    方式一次请求,就要做一次

  3,注册页面

  4,图形验证码

  5,用户名校验是否注册

  6,手机号码校验是否注册

  7,短信验证码

  8,注册保持用户数据

 

三,注册页面

  1,接口设计

    1-1,接口说明

      条目          说明

      请求方式        GET

      url定义         '/user/regiseter'

      参数格式        无参数

    1-2返回数据

      注册页面html

    2.后端代码

  

      2-1视图

 

from django.shortcuts import render
from django.views import View

def login(requset):
    return render(requset, 'user/login.html')


class RegisterView(View):
    """
    注册视图
    url:'/user/register'
    """
    def get(self, request):
        return render(request, 'user/register.html')

      2-2url

from django.urls import path
from . import views

app_name = 'user'

urlpatterns = [
    path('login/', views.login, name='login'),
    path('register/', views.RegisterView.as_view(), name='register'),
]

四,图像验证码

  1.接口设计

  1-1接口说明

    条目          说明

    请求方法        GET

    url定义         /iamge_code 

    参数格式        查询参数

  1.2参数说明

    参数名    类型    是否必须  描述

    rand     字符串    否    随机浮点数字支付串

   1-3返回数据

    验证码图片

 

验证码视图

import logging

from django.shortcuts import render

from utils.captcha.captcha import captcha
from . import constants
from django.http import HttpResponse

#日志器
logger =logging.getLogger('django')

def image_code_view(request):
    """
    生成验证码
    url:/image_code/
    :param request:
    :return:
    """
    #1.生成一个验证码,随机生成字符串,生成图片
    text, image = captcha.generate_captcha()
    #2.在后端保持验证码,为了等下它来校验
    #保存在session中
    request.session['image_code'] = text
    #给个过期时间
    request.session.set_expiry(constants.IMAGE_CODE_EXPIRES)
    #3.记录一个日志
    logger.info('Imag code: {}'.format(text))
    #4.返回验证记录
    return HttpResponse(content=image, content_type='image/jpg')

五,用户名校验功能

  1.接口设计

  1-1接口说明

    条目          说明

    请求方法        GET

    url定义         /username/(?P<username>\w{5,20})/

    参数格式        url路径参数

  1.2参数说明

    参数名      类型    是否必须  描述

    username     字符串    是    输入用户名

   1-3返回数据

    返回结果:

      {

      "errno": "0",

      "errmsg": "ok",

      "data": {

        "username": "username",    #查询用户名

        "count": 1           #用户查询数量

        }

      }

六、电话号码校验

  1.接口设计

  1-1接口说明

    条目          说明

    请求方法        GET

    url定义         /mobile/(?P<username>\1[3-9]\d{9})/

    参数格式        url路径参数

  1.2参数说明

    参数名      类型    是否必须  描述

    mobile     字符串    是      输入的手机号码

   1-3返回数据

    返回结果:

      {

      "errno": "0",

      "errmsg": "ok",

      "data": {

        "username": "mobile",    #查询手机号

        "count": 1           #手机号查询数量

        }

      }

七,json响应数据结构设计
  目的:
    1,减少代码坈余,提高复用性,解耦
    2,分工协作

   1,结构设计

    {"errno": "0", "errmsg": "ok", "data": ""}

    字段    

 

 

 

目标utils建立文件res_code
from django.http import JsonResponse
class Code:
    OK = "0"
    DBERR = "4001"
    NODATA = "4002"
    DATAEXIST = "4003"
    DATAERR = "4004"
    METHERR = "4005"
    SMSERROR = "4006"
    SMSFAIL = "4007"

    SESSIONERR = "4101"
    LOGINERR = "4102"
    PARAMERR = "4103"
    USERERR = "4104"
    ROLEERR = "4105"
    PWDERR = "4106"

    SERVERERR = "4500"
    UNKOWNERR = "4501"


error_map = {
    Code.OK: "成功",
    Code.DBERR: "数据库查询错误",
    Code.NODATA: "无数据",
    Code.DATAEXIST: "数据已存在",
    Code.DATAERR: "数据错误",
    Code.METHERR: "方法错误",
    Code.SMSERROR: "发送短信验证码异常",
    Code.SMSFAIL: "发送短信验证码失败",

    Code.SESSIONERR: "用户未登录",
    Code.LOGINERR: "用户登录失败",
    Code.PARAMERR: "参数错误",
    Code.USERERR: "用户不存在或未激活",
    Code.ROLEERR: "用户身份错误",
    Code.PWDERR: "密码错误",

    Code.SERVERERR: "内部错误",
    Code.UNKOWNERR: "未知错误",
}
def json_response(errno=Code.OK, errmsg='', data=None, kwargs=None):

    json_dict = {
        'errno': errno,
        'errmsg': errmsg,
        'data': data
    }
    if kwargs and isinstance(kwargs, dict):
        json_dict.update(kwargs)

    return JsonResponse(json_dict)

八、获取短信验证码

  1,业务流程分析
    校验手机号吗
    校验图像验证码
    校验是否在60s内有发送记录
    生成短信验证码
    发送短信
    保存这个短信验证码(保存在哪里?)
    保存发送记录

  2,接口设计
    2.1接口说明

      条目          说明

      请求方法        POST       #get查询/获取数据、post产生新数据

      url定义         /sms_code/

      参数格式        表单

    2.2参数说明

      参数名      类型    是否必须      描述

      mobile         字符串    是      输入的手机号码

      captcha     字符串    是      用户输入的图像验证码

     2.3返回数据

      返回结果:

      {

      "errno": "0",

      "errmsg": "发送短信验证码成功",

      }

后端:
  1,校验数据用form表单
  在verification目录下新建 forms表单

verification/views.py文件代码
class SmsCodeView(View):
    """
    发送短信验证码
    url: /sms_code/
    """

    def post(self, request):
        """
     生成短信验证码
     发送短信
     保存这个短信验证码(保存在哪里?)
     保存发送记录
        """
        form = CheckImageForm(request.POST, request=request)
if form.is_valid():
            #获取手机号码
            mobile = form.cleaned_data.get('mobile')
            #生成短信验证码
            sms_code = ''.join([random.choice('0123456789') for _ in range(constants.SMS_CODE_LENGTH)])
            #发送短信验证码 调用接口
            logger.info('发送短信验证码[正常][mobile: %s sms_code: %s ]' % (mobile, sms_code))
            #保持这个验证码  保持到redis
            #5分钟时效
            #创建短信验证码发送记录到key
            sms_flag_key = 'sms_flag_{}'.format(mobile)
            #创建短信验证码内容到key
            sms_text_key = 'sms_text_{}'.format(mobile)
            redis_conn = get_redis_connection(alias='verify_code')
            pl = redis_conn.pipeline()

            try:
                pl.setex(sms_flag_key, constants.SMS_CODE_INTERVAL, 1)
                pl.setex(sms_text_key,constants.IMAGE_CODE_EXPIRES*60, sms_code)
                #让管道通知redis执行命令
                pl.execute()
                return json_response(errmsg='短信验证码发送成功!')
            except Exception as e:
                logger.error('redis 执行异常: {}'.format(e))

                return json_response(errno=Code.UNKOWNERR, errmsg=error_map[Code.UNKOWNERR])
        else:
            #将表单的报错信息进行拼接
            err_msg_list = []
            for item in form.errors.values():
                err_msg_list.append(item[0])

            err_msg_str = '/'.join(err_msg_list)
            return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)
verification/forms.py文件代码
from django import forms
from django.core.validators import RegexValidator
from django_redis import get_redis_connection

from user.models import User

#创建手机号码正则校验器
mobile_validator = RegexValidator(r'^1[3,9]\d{9}$', '手机号码格式不正确')


class CheckImageForm(forms.Form):
    """
    校验手机号吗
    校验图像验证码
    校验是否在60s内有发送记录
    校验图形验证码
    """
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super().__init__(*args, **kwargs)

    mobile = forms.CharField(max_length=11, min_length=11, validators=[mobile_validator], error_messages={
        'max_length': '手机长度有误!',
        'min_length': '手机长度有误!',
        'required': '手机号码不能为空!',
    })

    captcha = forms.CharField(max_length=4, min_length=4, error_messages={
        'max_length': '图形验证码长度有误!',
        'min_length': '图形验证码长度有误!',
        'required': '图形验证码不能为空!',
    })

    def clean(self):
        clean_data = super().clean()
        mobile = clean_data.get('mobile')
        captcha = clean_data.get('captcha')
        # 如果前面到校验失败,mobile captcha 是 none
        # 如果前面到字段校验有问题,就不需要往下进行了
        if mobile and captcha:
            #1,校验图形验证码
            #获取session中把保存到验证码,和用户填入到进行比对
            image_code = self.requset.session.get('image_code')
            if not image_code:
                raise forms.ValidationError('图形验证码失效')
            if image_code.upper() != captcha.upper():
                raise forms.ValidationError('图形验证码校验失败!')

            #2,是否60秒以内发送过短信
            #存在redis里面
            redis_conn = get_redis_connection(alias='verify_code')
            if redis_conn.get('sms_flag_{}'.format(mobile)):
                raise forms.ValidationError('获取短信验证码过于频繁')

            #3,再次校验手机号号码是否注册
            if User.objects.filter(mobile=mobile).count():
                raise forms.ValidationError('手机号码已注册,请重新输入')
        return clean_data
static/js/user/registe.js文件代码
//5.发送短信验证码
let $smsButton = $('.sms-captcha');
$smsButton.click(()=>{
    // 拿到数据
    // 图形验证
    let sCaptcha = $('input[name="captcha_graph"]').val();
    if(sCaptcha === ''){
        message.showError('请输入图形验证码!');
        return
    }
    // 判断手机号码是否准备好
    if(!isMolibleReady){
        fnCheckMobile();
        return
    }

    $
        .ajax({
            url: '/sms_code/',
            type: 'POST',
            data: {
                mobile: $mobile.val(),
                captcha: sCaptcha
            },
            dataType: 'json'
        })
        .done((res)=>{
            if(res.errno !== '0'){
                message.showError(res.errmsg)
            }else {
                message.showSuccess(res.errmsg)
            }
        })
        .fail(()=>{
           message.showError('服务器超时,请重试!')
        });
})
static/js/base/common.js文件代码
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
    beforeSend: function (xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    }
});
templates/user/registe.html文件代码
{% block main_start %}
    <main id="container">
  <div class="register-contain">
    <div class="top-contain">
      <h4 class="please-register">请注册</h4>
      <a href="javascript:void(0);" class="login">立即登录 ></a>
    </div>
    <form action="" method="post" class="form-contain">
    {% csrf_token %}  #增加这个代码
九、注册功能
  1.业务流程分析
    1.1对参数进行校验
      1.校验用户名
      2.校验密码
      3.校验手机号码
      4.校验短信验证码
    1.2新建数据库记录
   
  2.接口设计
2.1接口说明

      条目          说明

      请求方法        POST       #get查询/获取数据、post产生新数据

      url定义         /user/register/

      参数格式        表单

    注意:post请求,一定要带上csrf_token

    2.2参数说明

      参数名         类型    是否必须      描述

      username          字符串    是      用户输入到用户名

      password      字符串    是      用户输入到密码

      passwrod_repeat  字符串    是      用户输入到重复密码

      mobile       字符串    是      用户输入到电话号码

      sms_code      字符串    是      用户输入到短信验证码

     2.3返回数据

      返回结果:

      {

      "errno": "0",

      "errmsg": "恭喜您,注册成功",

      }

user/views 代码
class RegisterView(View):
    """
    注册视图
    url:'/user/register'
    """

    def get(self, request):
        return render(request, 'user/register.html')

    def post(self, request):
        # 1.校验数据
        form = RegisterForm(request.POST)
        if form.is_valid():
            # 2.创建数据
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            mobile = form.cleaned_data.get('mobile')
            User.objects.create_user(username=username,
                                     password=password, mobile=mobile)
            return json_response(errmsg='恭喜您,注册成功')
        else:
            # 将表单的报错信息进行拼接
            err_msg_list = []
            for item in form.errors.values():
                err_msg_list.append(item[0])

            err_msg_str = '/'.join(err_msg_list)
            return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)


user/forms代码
from django import forms
from django_redis import get_redis_connection

from verification import constants
from .models import User
from verification.forms import mobile_validator


class RegisterForm(forms.Form):
    """
    用户注册表单
    """
    username = forms.CharField(label='用户名', max_length=20, min_length=5,
                               error_messages={
                                   'max_length': "用户名长度要小于20",
                                   'min_length': "用户名长度要大于5",
                                   'required': "用户名不能为空",
                               })
    password = forms.CharField(label='密码', max_length=20, min_length=6,
                               error_messages={
                                   'max_length': "密码长度要小于20",
                                   'min_length': "密码长度要大于6",
                                   'required': "密码不能为空",
                               })
    password_repeat = forms.CharField(label='密码', max_length=20, min_length=5,
                                      error_messages={
                                          'max_length': "密码长度要小于20",
                                          'min_length': "密码长度要大于5",
                                          'required': "密码不能为空",
                                      })
    mobile = forms.CharField(label='手机号', max_length=11, min_length=11,
                             validators=[mobile_validator, ],
                             error_messages={
                                 'max_length': "手机号码长度不正确",
                                 'min_length': "手机号码长度不正确",
                                 'required': "手机号不能为空",
                             })
    sms_code = forms.CharField(label='短信验证',
                               max_length=constants.SMS_CODE_LENGTH,
                               min_length=constants.SMS_CODE_LENGTH,
                               error_messages={
                                   'max_length': "短信验证码长度不正确",
                                   'min_length': "短信验证码长度不正确",
                                   'required': "短信验证码不能为空",
                               })

    def clean_username(self):
        """
        校验用户名
        :return:
        """
        username = self.cleaned_data.get('username')

        if User.objects.filter(username=username).exists():
            raise forms.ValidationError('用户名不存在')

        return username

    def clean_mobile(self):
        """
        单字段校验,用clean_mobile
        校验手机号码
        :return:
        """
        mobile = self.cleaned_data.get('mobile')

        if User.objects.filter(mobile=mobile).exists():
            raise forms.ValidationError('手机号已注册')

        return mobile

    def clean(self):
        """
        联合校验
        :return:
        """
        clean_data = super().clean()
        #校验密码是否一致
        password = clean_data.get('password')
        password_repeat = clean_data.get('password')

        if password != password_repeat:
            raise forms.ValidationError('两次密码不一致!')

        #校验短信验证码
        sms_code = clean_data.get('sms_code')
        mobile = clean_data.get('mobile')

        redis_conn = get_redis_connection(alias='verify_code')
        real_code = redis_conn.get('sms_text_{}'.format(mobile))
        if (not real_code) or (real_code.decode('utf-8') != sms_code):

            raise forms.ValidationError('短信验证码错误!')