欢迎来到Louis的博客

人生三从境界:昨夜西风凋碧树,独上高楼,望尽天涯路。 衣带渐宽终不悔,为伊消得人憔悴。 众里寻他千百度,蓦然回首,那人却在灯火阑珊处。
扩大
缩小

CRM---系统 登录 注册

Customer Relationship Managemen

一. 准备工作

1.新建一个IgCrm的Django项目

2.配置数据库

使用mysql,安装pymysql库,

'IgCrm.__init__.py'

import pymysql

pymysql.install_as_MySQLdb()
View Code

setting.py

# 数据库连接,数据库要先建好
DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'IgCrm',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': 3306
    }
}
View Code

3.扩展auth.User表

django自带的User表,有时满足不了我们的需求,我们可以使用继承的方式扩展User表

先配置setting.py,指定那张表管理Django用户认证

setting

# 指定Django使用那张表进行用户验证管理
AUTH_USER_MODEL = "app01.UserInfo"
View Code

 

modle.py

from django.db import models
# 默认的User表也继承于这个类
from django.contrib.auth.models import AbstractUser


# Create your models here.

# 继承关系
class UserInfo(AbstractUser):
    telephone = models.CharField(max_length=16)
    address = models.CharField(max_length=60)
    gender = models.BooleanField()
View Code

执行命令迁移数据库

  • makemigrations
  • migrate

 

可以发现之前的auth_user表没有了,是由于我们使用了app01_userinfo作为用户认证的表,userinfo表即继承了原来user表的字段,也有了自定义的字段。

4.配置静态资源目录

setting.py

STATIC_URL = '/static/'

# 使用static别名,制定静态资源路径
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]
View Code

5.下载一套后台模版

将模版文件夹中的html文件放到templates目录,css,js,fonts,img等静态资源文件放到static目录下

二.登录功能

1.需求:

  • 基于ajax请求的页面无刷新登录
  • 实现验证码功能

2.修改login.html模版

模版原样式:

原模版代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="Mosaddek">
    <meta name="keyword" content="FlatLab, Dashboard, Bootstrap, Admin, Template, Theme, Responsive, Fluid, Retina">
    <link rel="shortcut icon" href="img/favicon.html">

    <title>FlatLab - Flat & Responsive Bootstrap Admin Template</title>

    <!-- Bootstrap core CSS -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/bootstrap-reset.css" rel="stylesheet">
    <!--external css-->
    <link href="assets/font-awesome/css/font-awesome.css" rel="stylesheet" />
    <!-- Custom styles for this template -->
    <link href="css/style.css" rel="stylesheet">
    <link href="css/style-responsive.css" rel="stylesheet" />

    <!-- HTML5 shim and Respond.js IE8 support of HTML5 tooltipss and media queries -->
    <!--[if lt IE 9]>
    <script src="js/html5shiv.js"></script>
    <script src="js/respond.min.js"></script>
    <![endif]-->
</head>

  <body class="login-body">

    <div class="container">

      <form class="form-signin" action="index.html">
        <h2 class="form-signin-heading">sign in now</h2>
        <div class="login-wrap">
            <input type="text" class="form-control" placeholder="User ID" autofocus>
            <input type="password" class="form-control" placeholder="Password">
            <label class="checkbox">
                <input type="checkbox" value="remember-me"> Remember me
                <span class="pull-right"> <a href="#"> Forgot Password?</a></span>
            </label>
            <button class="btn btn-lg btn-login btn-block" type="submit">Sign in</button>
            <p>or you can sign in via social network</p>
            <div class="login-social-link">
                <a href="index.html" class="facebook">
                    <i class="icon-facebook"></i>
                    Facebook
                </a>
                <a href="index.html" class="twitter">
                    <i class="icon-twitter"></i>
                    Twitter
                </a>
            </div>

        </div>

      </form>

    </div>


  </body>
</html>
View Code

 

修改后的模版代码:

  • 修改外链资源的路径
  • 修改form表单提交按钮的input标签的type为button
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="Mosaddek">
    <meta name="keyword" content="FlatLab, Dashboard, Bootstrap, Admin, Template, Theme, Responsive, Fluid, Retina">
    <link rel="shortcut icon" href="/static/img/favicon.html">

    <title>FlatLab - Flat & Responsive Bootstrap Admin Template</title>

    <!--注意原模版中的外链资源,路径地址全部都要修改-->
    <!-- Bootstrap core CSS -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/bootstrap-reset.css" rel="stylesheet">
    <!--external css-->
    <link href="/static/assets/font-awesome/css/font-awesome.css" rel="stylesheet"/>
    <!-- Custom styles for this template -->
    <link href="/static/css/style.css" rel="stylesheet">
    <link href="/static/css/style-responsive.css" rel="stylesheet"/>
    <script src="/static/js/jquery.js"></script>
    <!-- HTML5 shim and Respond.js IE8 support of HTML5 tooltipss and media queries -->
    <!--[if lt IE 9]>
    <script src="/static/js/html5shiv.js"></script>
    <script src="/static/js/respond.min.js"></script>
    <![endif]-->
</head>

<body class="login-body">

<div class="container">
    <form class="form-signin" action="index.html">
        <h2 class="form-signin-heading">sign in now</h2>
        <!--由于是post请求,需要加入csrf_token通过验证-->
        {% csrf_token %}
        <div class="login-wrap">
            <!--用户名-->
            <input type="text" class="form-control" required placeholder="User ID" id="user" autofocus>
            <!--密码-->
            <input type="password" class="form-control" id="password" placeholder="Password">
            <!--验证码-->
            <div class="row">
                <div class="col-md-6">
                    <!--用户输入验证码-->
                    <input type="text" class="form-control" id="code" placeholder="验证码" autofocus>
                </div>
                <div class="col-md-6">
                    <!--验证码图片-->
                    <img src="/get_valid_code/" alt="" id="valid-code" title="点击刷新验证码">
                </div>
            </div>
            <label class="checkbox">
                <input type="checkbox" value="remember-me"> Remember me
                <span class="pull-right"> <a href="#"> Forgot Password?</a></span>
            </label>
            <!--如果用form表单还想发送ajax请求,必须把使用input标签type为button的按钮,input标签type为submit和button标签都会触发form的action操作-->
            <input type="button" class="btn btn-lg btn-login btn-block" id="login-btn" value="Sign in">
            <!--显示验证验证错误信息-->
            <span id="error-msg"></span>
            <p>or you can sign in via social network</p>
            <div class="login-social-link">
                <a href="index.html" class="facebook">
                    <i class="icon-facebook"></i>
                    Facebook
                </a>
                <a href="index.html" class="twitter">
                    <i class="icon-twitter"></i>
                    Twitter
                </a>
            </div>
        </div>
    </form>
</div>
<!--引用自己写的JS文件-->
<script src="/static/js/owner_js/login.js"></script>
</body>
</html>
View Code

 

 

3.验证码生成

  • 验证码生成基于PIL模块完成,安装pillow就可以使用PIL了

验证码生成代码

validcode.py

from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import random


def get_valid_img():
    # 随机颜色
    def get_random_color():
        return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)

    # 创建画布
    img = Image.new("RGB", (130, 38), get_random_color())
    # 创建画笔
    draw = ImageDraw.Draw(img)
    # 设置所使用的字体
    font = ImageFont.truetype("static/fonts/kumo.ttf", 32)

    # 保存验证码对应的字符
    keep_str = ""

    # 开始绘画
    for i in range(4):
        # 随机数字
        random_num = str(random.randint(0, 9))
        # 随机小写字母
        random_lowalf = chr(random.randint(97, 122))
        # 随机大写字母
        random_upperalf = chr(random.randint(65, 90))
        # 4个随你字符中选取一个
        random_char = random.choice([random_num, random_lowalf, random_upperalf])
        # 参数说明: 1.字符位于画布中的坐标,画布左上角为0,0   2.字符   3.设置画布背景颜色   4.字符所使用字体
        draw.text((i * 30 + 20, 0), random_char, get_random_color(), font=font)
        keep_str += random_char

    ## 随机线条
    # width = 130
    # height = 38
    # for i in range(10):
    #     x1 = random.randint(0, width)
    #     x2 = random.randint(0, width)
    #     y1 = random.randint(0, height)
    #     y2 = random.randint(0, height)
    #     draw.line((x1, y1, x2, y2), fill=get_random_color())
    ## 随机噪点
    # for i in range(100):
    #     draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
    #     x = random.randint(0, width)
    #     y = random.randint(0, height)
    #     draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
    # 创建内存对象
    f = BytesIO()
    # 将生成的验证码图片保存到内存对象中
    img.save(f, "png")

    # 获取到这个内存对象
    data = f.getvalue()

    # 返回内存对象,和验证码对应的字符串
    return data, keep_str
View Code

4.设置路由

urls.py

from django.contrib import admin
from django.urls import path

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 登录
    path('login/', views.login, name='login'),
    # 验证码
    path('get_valid_code/', views.get_valid_code),
    path('', views.index)
]
View Code

 

5.视图函数

  • 使用session存储生成的验证码字符串,用户提交alax请求带着session_id过来的,取出对应的验证码字符串,进行匹配
  • 返回最终验证数据,使用了JsonResponse模块,减少了我们封装json数据的过程,JsonResponse给响应头封装了一个content_type='application/json'的响应头,客户端接受到响应后会自动根据这个响应头进行处理,无需我们再进行反序列化
  • 使用django认证组件进行验证用户
from django.shortcuts import render, redirect, HttpResponse
from django.contrib import auth
from django.http import JsonResponse
from django.urls import reverse
from app01 import validcode


# Create your views here.


# 用户登录
def login(request):
    # 客户端提交的ajax请求
    if request.is_ajax():
        # 获取客户端上传上来的数据
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        code = request.POST.get('code')
        # 返回给客户端的数据字典
        response = {'user': None, 'error_msg': ''}
        # 验证码是否匹配
        if code.upper() == request.session.get('keep_str').upper():
            # 验证用户名
            if auth.authenticate(username=user, password=pwd):
                response['user'] = user
            else:
                response['error_msg'] = '用户名或密码错误'
        else:
            response['error_msg'] = '验证码错误'
        # 使用JsonResponse返回一个json字符串数据
        return JsonResponse(response)
    else:
        return render(request, 'login.html')


# 跳转login
def index(request):
    return redirect(reverse('login'))


# 返回验证码
def get_valid_code(request):
    # 调用方法返回验证码图片内存对象,和验证码字符串
    code_img, keep_str = validcode.get_valid_img()
    # 使用验证码字符串设置session
    # 该操作的进行的3个步骤
    # 1.将验证码字符串变成键值对并加密插到session中生成一条记录,键值对字段名为session_data,并给该条记录的session_key字段添加一个随机字符串,如果表中已存在对应的session_key已存在则更新session_data
    # 2.返回session_key
    # 2.给响应体添加一个session_id=session_key的cookie
    request.session['keep_str'] = keep_str
    # 返回验证码响应体
    return HttpResponse(code_img)
View Code

6.前端JS代码

$(function () {
    // 刷新验证码
    $('#valid-code').click(function () {
        this.src += '?'
    });
    $('#login-btn').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            data: {
                user: $('#user').val(),
                pwd: $('#password').val(),
                code: $('#code').val(),
                csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
            },
            success: function (response) {
                if (response.user) {
                    location.href = 'http://www.baidu.com'
                } else {
                    $('#error-msg').text(response.error_msg).css('color', 'red')
                }
            }
        })
    });

// 输入框获得焦点,移除错误信息
    $('#user,#password,#code').focus(function () {
        $('#error-msg').text('')
    });
});
View Code

 

三. 注册功能

1.需求

  • 基于ajax的数据提交
  • 基于django forms的表单数据校验,及表单标签渲染

2.新增路由

urls.py

    # 注册
    path('reg/', views.reg, name='reg'),
View Code

 

3. 创建forms表单组件

forms_check.py

from django import forms
from django.forms import widgets
from app01.models import UserInfo
from django.core.exceptions import ValidationError
import re


# 创建一个forms组件类
class FormCheck(forms.Form):
    # 用户名
    username = forms.CharField(min_length=4,
                               max_length=16,
                               label='用户名:',
                               error_messages={'required': '用户名不能为空',
                                               'min_length': '用户名不能少于4位'},
                               widget=widgets.TextInput(attrs={'placeholder': "用户名为4~16为位"})
                               )
    # 校验密码
    password = forms.CharField(min_length=6,
                               max_length=16,
                               label='密码:',
                               error_messages={'required': '密码不能为空',
                                               'min_length': '密码不能少于6位'},
                               widget=widgets.PasswordInput(attrs={'placeholder': "密码为6~16位"})
                               )
    # 校验确认密码
    confirm_password = forms.CharField(min_length=6,
                                       max_length=16,
                                       label='确认密码:',
                                       error_messages={'required': '密码不能为空',
                                                       'min_length': '密码不能少于6位'},
                                       widget=widgets.PasswordInput(attrs={'placeholder': "请确认密码"}))
    # 校验邮箱
    email = forms.EmailField(error_messages={"required": "邮箱不能为空", "invalid": "邮箱格式错误"},
                             label='邮箱',
                             widget=widgets.EmailInput(attrs={'placeholder': "请输入邮箱邮箱"}))
    # 校验手机号
    telephone = forms.CharField(label='手机号码:',
                                error_messages={'required': '手机号不能为空'},
                                widget=widgets.TextInput(attrs={'placeholder': "请输入手机号"}))

    # 给所有字段添加form-control类
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for filed in self.fields.values():
            filed.widget.attrs.update({'class': 'form-control'})

    # 校验用户名是否已存在(局部钩子)
    def clean_username(self):
        val = self.cleaned_data.get('username')
        if UserInfo.objects.filter(username=val).first():
            raise ValidationError('用户名已存在')
        else:
            return val

    # 校验密码不能位纯数字
    def clean_password(self):
        val = self.cleaned_data.get('password')
        if val.isdigit():
            raise ValidationError('密码不能为纯数字')
        else:
            return val

    # 校验邮箱格式
    def clean_email(self):
        val = self.cleaned_data.get('email')
        if re.search(r'^[\w.\-]+@(?:[a-z0-9]+(?:-[a-z0-9]+)*\.)+[a-z]{2,3}$', val):
            return val
        else:
            raise ValidationError('无效的邮箱')

    # 校验手机号格式
    def clean_telephone(self):
        val = self.cleaned_data.get('telephone')
        if re.search(r'^(13\d|14[5|7]|15\d|166|17[3|6|7]|18\d)\d{8}$', val):
            return val
        else:
            raise ValidationError('无效的手机号')

    # 校验两次密码是否一致(全局钩子)
    def clean(self):
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        if password and confirm_password and password != confirm_password:
            self.add_error('confirm_password', ValidationError('两次密码不一致'))
        else:
            return self.cleaned_data
View Code

4.视图函数

views.py

# 用户注册
def reg(request):
    # 接收到ajax请求
    if request.is_ajax():
        # 使用forms对象对post请求体中的数据进行校验
        form_check = FormCheck(request.POST)
        # 响应的数据字典
        response = {'user': None, 'error_msg': ''}
        # 所有表单校验都通过
        if form_check.is_valid():
            # 到数据库插入一条用户记录数据
            username = request.POST.get('username')
            password = request.POST.get('password')
            email = request.POST.get('email')
            telephone = request.POST.get('telephone')
            UserInfo.objects.create_user(username=username, password=password, email=email, telephone=telephone)
            # 响应体数据字典的user的None改为用户名
            response['user'] = form_check.cleaned_data.get('username')
        else:
            # 有表单验证错误的情况,响应体数据字典的error_msg设置值为校验错误信息
            response['error_msg'] = form_check.errors
        # 最后返回响应体数据字典
        return JsonResponse(response)
    else:
        # 创建forms对象
        forms = FormCheck()
        # 返回页面及forms对象,到页面进行标签渲染
        return render(request, 'reg.html', locals())
View Code

5.前端页面

  • 先写静态标签页面,调好样式,然后再使用forms组件修改渲染页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="Mosaddek">
    <meta name="keyword" content="FlatLab, Dashboard, Bootstrap, Admin, Template, Theme, Responsive, Fluid, Retina">
    <link rel="shortcut icon" href="/static/img/favicon.html">

    <title>FlatLab - Flat & Responsive Bootstrap Admin Template</title>

    <!--注意原模版中的外链资源,路径地址全部都要修改-->
    <!-- Bootstrap core CSS -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/bootstrap-reset.css" rel="stylesheet">
    <!--external css-->
    <link href="/static/assets/font-awesome/css/font-awesome.css" rel="stylesheet"/>
    <!-- Custom styles for this template -->
    <link href="/static/css/style.css" rel="stylesheet">
    <link href="/static/css/style-responsive.css" rel="stylesheet"/>
    <script src="/static/js/jquery.js"></script>
    <!-- HTML5 shim and Respond.js IE8 support of HTML5 tooltipss and media queries -->
    <!--[if lt IE 9]>
    <script src="/static/js/html5shiv.js"></script>
    <script src="/static/js/respond.min.js"></script>
    <![endif]-->
</head>

<body class="login-body">

<div class="container">
    <form class="form-horizontal form-signin form-signup" action="index.html">
        <h2 class="form-signin-heading">sign up now</h2>
        <!--由于是post请求,需要加入csrf_token通过验证-->
        {% csrf_token %}
        <div class="login-wrap">
            <!--遍历forms对象中的每个字段生成input标签-->
            {% for field in forms %}
                <div class="form-group">
                    <!--field.auto_id为对应的input的id属性-->
                    <!--field.label后端该字段设置的label值-->
                    <label for="{{ field.auto_id }}"
                           class="col-sm-2 control-label signup-label">{{ field.label }}</label>
                    <div class="col-sm-10">
                        <!--field为input字段-->
                        {{ field }}
                        <span class="error-msg pull-right"></span>
                    </div>
                </div>
            {% endfor %}
            <!--密码-->
            <!--
            <input type="password" class="form-control" required id="password" placeholder="密码">
            -->
            <!--如果用form表单还想发送ajax请求,必须把使用input标签type为button的按钮,input标签type为submit和button标签都会触发form的action操作-->
            <a href="/login/" class="pull-right re-login">已有帐号?马上登录</a>
            <input type="button" class="btn btn-lg btn-login btn-block" id="signup-btn" value="提交">
            <!--显示验证验证错误信息-->

        </div>
    </form>
</div>
<!--引用自己写的JS文件-->
<script src="/static/js/owner_js/reg.js"></script>
</body>
</html>
View Code

 

6.前端js代码

$(function () {
    // 点击注册发送ajax请求
    $('#signup-btn').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            data: {
                username: $('#id_username').val(),
                password: $('#id_password').val(),
                confirm_password: $('#id_confirm_password').val(),
                email: $('#id_email').val(),
                telephone: $('#id_telephone').val(),
                csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val()
            },
            success: function (response) {
                if (response.user) {
                    alert('注册成功!');
                    location.href = '/login/'
                } else {
                    // 渲染报错信息
                    $.each(response.error_msg, function (i, j) {
                        $('#id_' + i).next().html(j[0]).css('color','red').parent().addClass('has-error')
                    })
                }
            }
        })
    });
    // input获得鼠标焦点,移除校验错误
    $('.login-wrap input').focus(function () {
        $(this).next().html('').parent().parent().removeClass('has-error')
    })
});
View Code

 

posted on 2018-11-05 21:57  Louiszj  阅读(1145)  评论(0)    收藏  举报

导航