Django商城项目实战之注册界面

项目架构

  • 前后端分离
  • 前端 : HTML5 、css、js
  • 后端 : DRF、Django1.11.11、python3.5

开始创建项目文件,和一些配置

创建项目

django-admin startproject 项目名

创建app

python manage.py startapp app的名字

然后创建对应的文件夹,为了分类各种文件内容

在这里插入图片描述

1.MySQL数据库配置

settings文件里:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'shanghui',
        'USER': 'yy',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

项目的同名文件夹下的__init__().py文件:

import pymysql
pymysql.install_as_MySQLdb()

2.Redis数据库配置

settings文件里:

# 这是redis作为session的保存数据库的设置
CACHES = {
    'default':{
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS':{
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    },
    'session':{
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/2',
        'OPTIONS':{
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'session'
# 修改session的机制,使用redis进行保存,并且使用的是session对应的redis配置

3.跨域请求问题配置

安装:

pip install django-cors-headers

settings文件添加:

INSTALLED_APPS = [
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # 最好添加至第一行
]

CORS_ORIGIN_WHITELIST = (
    # 这里设置域名
    'http://127.0.0.1:8080',
)

CORS_ALLOW_CREDENTIALS = True  #允许携带cookie

4.自定义异常处理配置

settings文件配置:

# 异常处理配置,后面是这个exceptions自定义异常的文件的路径
REST_FRAMEWORK = {
   'EXCEPTION_HANDLER':'shanghuiproject.utils.exceptions.exception_handler',
}

先在utils文件夹里创建exceptions.py文件,然后编写下面内容:

from rest_framework.views import exception_handler as drf_handler
from rest_framework.response import Response
from rest_framework.status import HTTP_500_INTERNAL_SERVER_ERROR

def exception_handler(exc, content):
    # 如果框架能处理的异常,还是让框架处理
    response = drf_handler(exc, content)

    # 框架不能处理的异常,自己处理
    if response is None:
        # 项目出错了,但是DRF框架对出错的异常没有处理
        # 可以在此处对异常进行统一处理,比如:保存出错信息到日志文件

        view = content['view'] # 出错的视图
        error = '服务器内部错误, %s' % exc
        print('%s: %s' %(view, error))

        # return Response({'detail': error}, status=500)
        response = Response({'message': '服务器错误'}, status=HTTP_500_INTERNAL_SERVER_ERROR)

    return response

创建用户模型,可以使用django用户认证模型

优点:可以自动验证,并省了自己写字段
在users文件下的 models.py内容

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

# Create your models here.

class User(AbstractUser):
    phone = models.CharField(max_length=11,unique=True, verbose_name='手机号码')

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

然后在自己的settings文件里添加:

# 声明使用的是django自带的认证系统
AUTH_USER_MODEL = 'users.User'

登录页面html和css

在这里插入图片描述
这里主要讲后端内容,上面为我写完html和css内容后的效果图,当然在调试的时候还需要启动前端服务器。前端服务器的安装配置方法:https://blog.csdn.net/dakengbi/article/details/90764642

前端js文件内容:

//生成UUID
var myuuid = '';
function uuid() {
    var s = [];
    var hexDigits = "0123456789abcdef";
    for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = "-";

    var uuid = s.join("");
    return uuid;
}

//获取图片验证码
function getCode() {
    myuuid = uuid();
    var image_url = 'http://127.0.0.1:8000/image_code/' + myuuid ;
    $('.piccode img').attr('src', image_url);
}

//获取短信验证码,同时校验图片验证码是否正确
//校验用户名
//密码和输入密码是否一致,且密码还有格式
function getMsgCode() {
    // 0.获取值
    var phone = $('#phone').val();  //手机号码
    var html_text = $('#logintext1').val();  //图片验证吗

    //1.1 获取到的数据进行正则的校验
    var reg_uuid = /^[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}$/;

    //发送GET数据,验证图片验证码
    if(phone_flag && imageCode_flag) {
        $('#errortext').text('');
        var msgurl = 'http://127.0.0.1:8000/image_code/msg_code/?phone=' + phone + '&html_text=' + html_text + '&myuuid=' + myuuid;
        // 2.ajax请求
        $.ajax({
            url: msgurl,
            method: 'GET',
            contentType: 'application/json',

            success: function (data) {
                console.log(data);
                $('#errortext').text(data);
            },
            error: function (data) {
                console.log(data);
            }
        })
    }
    else{
        $('#errortext').text('手机号码或者图片验证码不正确');
    }
}

//判断密码格式是否正确
var pwd1_flag = false;
function passwordInputBlur() {
    // 验证密码
    var pwd1 = $('#pwd1').val();
    var reg_pwd1 = /^[a-z A-Z 0-9]{6,16}$/;

    // console.log(reg_pwd1.exec(pwd1));

     //密码校验
    if(!reg_pwd1.test(pwd1)){
        $('#pwd1_error').text('密码格式不正确');
        pwd1_flag = false;
    }
    else{
        $('#pwd1_error').text('');
        pwd1_flag = true;
    }
}

//判断再次输入的密码是否和第一次一样
var pwd2_flag = false;
function passwordAgainInputBlur() {
    var pwd2 = $('#pwd2').val();
    var pwd1 = $('#pwd1').val();
        //第二次密码校验
    if(pwd1_flag){
        if(pwd1 != pwd2){
            $('#pwd2_error').text('第二次密码输入不正确');
            pwd2_flag = false;
        }
        else {
            $('#pwd2_error').text('');
            pwd2_flag = true;
        }
    }
}

//判断手机号码是否正确
var phone_flag = false;
function phoneInputBlur() {
    var phone = $('#phone').val();  //手机号码
    var reg_phone = /^1[3456789]\d{9}$/;
    // console.log(reg_phone.exec(phone)); // 正则匹配结果

    // 1.2 利用正则进行校验,如果不通过则弹窗
    if(!reg_phone.test(phone)){
        $('#phoneerror').text('手机号码校验未通过');
        phone_flag = false;
    }
    else{
        $('#phoneerror').text('');
        phone_flag = true;
    }
}

//判断用户名的格式是否正确
var username_flag = false;
function usernameInputBlur() {
    var username = $('#username').val();
    var reg_username = /^[\w]+$/;
    console.log(reg_username.exec(username));
    //用户名校验
    if(!reg_username.test(username)){
        $('#username_error').text('用户名不正确');
        username_flag = false;
    }
    else{
        $('#username_error').text('');
        username_flag = true;
    }
}

//图片验证码输入框失去焦点事件
var imageCode_flag = false;
function imageTextBlur() {
    var html_text = $('#logintext1').val();
    var reg_text = /^[\w]{4}$/;
    if(!reg_text.test(html_text)){
        $('#imagecode_error').text('图片验证码要为4位');
        imageCode_flag = false;
    }else {
        $('#imagecode_error').text('');
        imageCode_flag = true;
    }

    //通过ajax发送数据到后端
    if (html_text && imageCode_flag) {
        var data = {
            'uuid' : myuuid,
            'code' : html_text,
        };
        data = JSON.stringify(data);

        $.ajax({
            url:'http://127.0.0.1:8000/image_code/1235/',
            type:'POST',
            contentType: 'application/json',
            data:data,

            success: function (data) {
                console.log(data);
                if(data=='ok'){
                    $('#imagecode_error').text('');
                    imageCode_flag = true;
                }
                else{
                    $('#imagecode_error').text('图片验证码错误');
                    imageCode_flag = false;
                }
            },
            error: function (data) {
                console.log(data);
                $('#imagecode_error').text('图片验证码错误');
                imageCode_flag = false;
            }
        })
    }
}

//判断邮箱格式是否正确
var email_flag = false;
function emailInputBlur() {
    var email = $('#email').val();
    // var reg_email = /[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/;
    var reg_email = /^[\w]+@[\w]+\.com$/;
    console.log(reg_email.exec(email));
    if (!reg_email.test(email)){
        $('#emailerror').text('邮箱格式错误');
        email_flag = false;
    }
    else{
        $('#emailerror').text('');
        email_flag = true;
    }
}

//注册点击事件
function submitChick() {
    // todo 获取所有需要提交的数据
    var username = $('#username').val();
    var pwd1 = $('#pwd1').val();
    var pwd2 = $('#pwd2').val();
    var phone = $('#phone').val();
    var email = $('#email').val();
    var messageCode = $('#logintext2').val();

    // todo 显示效果
    $('#errortext').text('');

    // todo 将获取的数据进行校验
    if (messageCode && username_flag && pwd2_flag && phone_flag && email_flag){
        // todo 将所有的数据拼接成json数据
        var data = {
            'username':username,
            'password':pwd1,
            'pwdagain':pwd2,
            'phone':phone,
            'email': email,
            'messagecode':messageCode
        };
        data = JSON.stringify(data);

        $.ajax({
            // todo 使用ajax提交POST请求
            url: 'http://127.0.0.1:8000/users/myusers/',
            method: 'POST',
            contentType:  'application/json',
            data: data,

            success:function (data) {
                $('#errortext').text('注册成功');
                console.log(data);
            },
            error: function (data) {
                $('#errortext').text('注册失败');
                console.log(data);
            }
        })
    }
}

//入口函数
$(document).ready(function () {
    getCode();
});

后端生成验证码图片,当得到前端GET请求时返回验证码图片

在django项目新建一个模块image_code, 在里面的views.py文件内容:

import random
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
import redis

from shanghuiproject.libs.yuntongxun.sms import CCP
from shanghuiproject.libs.captcha.captcha import captcha
from .constants import *
from . import serializers
# Create your views here.

class ImageView(APIView):
    conn = redis.Redis(host='localhost', port=6379, db=1, password=None)

    # 前段获取验证码图片
    def get(self,request,image_id):
        # 1.生成验证吗
        text, image = captcha.generate_captcha()
        # print('---------->', image_id)
        # 2.要保存验证吗上面的文字部分
        self.conn.setex(image_id, IMAGE_UUID_REDIS_EXPIRES, text)
        # 3.返回图片
        return HttpResponse(image, content_type='image/jpg')
    # 图片验证码post验证是否正确
    def post(self,request,image_id):

        # print(request.data)

        uuid = request.data.get('uuid')
        code = request.data.get('code').upper()
        redis_data = self.conn.get(uuid)
        if redis_data:
            if redis_data.decode() == code:
                return Response('ok')
        return Response('wrong')

当点击获取短信验证码按钮时,后端会对手机号和图片验证码进行检测,如果通过就发送短信验证码:

在django项目里的image_code模块, 在里面的views.py文件内容添加:

class MsgView(GenericAPIView):
    serializer_class = serializers.ImageTextSerializers

    def get(self,request):

        # 1.校验图片验证码
        print('-------->>>>', request.query_params)
        query_dict = request.query_params
        serializer = self.get_serializer(data=query_dict)
        serializer.is_valid(raise_exception=True)

        # 2.在5分钟不再重复发送(利用redis可以设置过期时间的特点,将表示在5分钟内发送的标准设置放入redis)
        # 也就是说5分钟内如果能在redis中找到数据,就是代表着重复操作
        # redis 中标志的例子1311111111111_flag: 1

        # 3.生成短信验证码
        msg = '%04d' %random.randint(0,9999)
        print('生成的短信验证码', msg)

        # 4.保存短信验证码
        # todo 2.1 连接校验专用的redis数据
        conn = redis.Redis(host='localhost', port=6379, db=1, password=None)
        # todo 2.2 插入数据库到redis,但是要设置过期时间
        # todo 保存短信证码,将来注册的时候需要进行对比
        # 获取手机号码
        phone = query_dict['phone']

        conn.setex('%s_msg'%phone, PHONE_MSG_CODE_EXPIRES,msg) # 保存手机短信信息和相对应的手机号,信息有效时间为5分钟。
        conn.setex('%s_flag'%phone,MSG_CODE_REQUEST,msg) # 让前端发送数据的标签 2分钟才可以发送一次。

        # 5.发送手机短信
        # todo 这里采用容联云 ,这个第三方模块来发送短信
        # send_template_sms(self, to, datas, temp_id):
        ccp = CCP()
        ret_data = ccp.send_template_sms(phone,[msg,2],1)
        print(ret_data)

        # 6.返回相应的相应数据
        return Response('短信已发送')

urls.py路径跳转内容:

from django.conf.urls import url
from . import views
# . 是当前路径下找

urlpatterns = [
    url(r'^msg_code/$', views.MsgView.as_view()),
    url(r'^(?P<image_id>[\w|-]+)/$', views.ImageView.as_view()),
]

新建一个serilizers.py文件来写数据的校验逻辑(如手机号码和图片验证码)

from rest_framework import serializers
import redis

class ImageTextSerializers(serializers.Serializer):
    myuuid = serializers.UUIDField()
    phone = serializers.CharField(max_length=11,min_length=11)
    html_text = serializers.CharField(max_length=4,min_length=4)

    def validate(self, attrs):
        uuid = attrs['myuuid']
        phone = attrs['phone']
        text = attrs['html_text'].upper()

        conn = redis.Redis(host='localhost', port=6379, db=1, password=None)
        # todo 将用户输入的验证码与真实的验证码对比
        redis_text = conn.get('%s'%uuid)

        # redis数据可能会失效,如果没失效,就把值从bytes转成str
        if redis_text:
            redis_text = redis_text.decode()

        if text != redis_text:
            raise serializers.ValidationError('图片验证码错误')

        # todo 取出该手机号码对应的发送状态,如果能取到,说明已经在5分钟内发过了
        flag = conn.get('%s_flag'%phone)
        if flag:
            raise serializers.ValidationError('在5分钟内请勿重复发送')

        return attrs

接收前端的用户信息,并进行校验,最后把用户信息存到数据库

用之前创建好的users模块,在views.py文件里的内容为:

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
import redis

class UserRegisterView(CreateAPIView):
    serializer_class = serializers.RegisterUserSerializer

urls.py文件:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^myusers/$', views.UserRegisterView.as_view()),
]

新建一个seralizers.py文件来对用户信息进行校验:

前后端都需要校验,因为有软件可以绕过前端直接向后端发送请求

from rest_framework import serializers
import re
from django_redis import get_redis_connection
from . import models


class RegisterUserSerializer(serializers.ModelSerializer):
    # todo 因为User模型里面没有再次输入密码和短信信息,所以这里需要补充
    # 第二次密码,短信信息
    pwdagain = serializers.CharField(max_length=12,min_length=6,write_only=True)
    messagecode = serializers.CharField(max_length=4,min_length=4,write_only=True)

    class Meta:
        model = models.User
        # todo 上面补充的字段也是需要写到下面的序列化器的
        fields = ('username','email','password','phone','pwdagain','messagecode',)

    def validate(self, attrs):

        # 验证用户名是不是符合要求
        if not re.match('^[\w]+$', attrs['username']):
            raise serializers.ValidationError('用户名输入格式不对')
        # 验证邮箱是不是符合要求
        if not re.match('^[\w]+@[\w]+\.com$', attrs['email']):
            raise serializers.ValidationError('请输入合法的邮件地址')
        # 验证密码是不是符合要求
        if not re.match('^[a-z A-Z 0-9]{6,16}$', attrs['password']):
            raise serializers.ValidationError('密码格式不对')
        # 验证再次输入密码是否和第一次输入的密码相同
        if attrs['password']!=attrs['pwdagain']:
            raise serializers.ValidationError('第二次输入密码不一致')
        # 验证手机号码格式
        if not re.match('^1[3456789]\d{9}$', attrs['phone']):
            raise serializers.ValidationError('手机号码格式不对')
        # 验证短信信息验证码是否正确
        conn = get_redis_connection('default')

        if conn.get('%s_msg'%attrs['phone']):
            mycode = conn.get('%s_msg'%attrs['phone']).decode()
            if mycode != attrs['messagecode']:
                raise serializers.ValidationError('短信验证码不正确')
        else:
            raise serializers.ValidationError('请先获取短信验证码')

        return attrs

    def create(self, validated_data):

        print('此时的validated_data里面包含短信信息和再次输入密码')
        print(validated_data)
        del validated_data['pwdagain']
        del validated_data['messagecode']
        print(validated_data)

        # 此时的 validated_data将不再包含 pwdagain 和 messagecode
        user_obj = models.User.objects.create(**validated_data)

        return user_obj

猜你喜欢

转载自blog.csdn.net/dakengbi/article/details/91988589