一、创立项目

1.1.开立项目和app

django-admin startproject mysite_login

python manage.py startapp login

1.2.安装时区和语言

Django暗中认可使用美利坚合众国岁月和罗马尼亚(România)语,在档案的次序的settings文件中,如下所示:

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

作者们把它改为亚洲/上海日子和国文

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

1.3.启动

运转测量检验一下工程,在本机的浏览器中做客http://127.0.0.1:8000/

图片 1

 二、设计数据模型

 2.1.数据库模型设计

 作为二个顾客登入和注册类型,需求保留的都以各个客户的相干新闻。很驾驭,我们足足必要一张顾客表User,在客户表里需求保留上边的音信:

  • 用户名
  • 密码
  • 邮箱地址
  • 性别
  • 创立时间

 走入login/models.py,代码如下

# login/models.py

from django.db import models


class User(models.Model):
    '''用户表'''

    gender = (
        ('male','男'),
        ('female','女'),
    )

    name = models.CharField(max_length=128,unique=True)
    password = models.CharField(max_length=256)
    email = models.EmailField(unique=True)
    sex = models.CharField(max_length=32,choices=gender,default='男')
    c_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['c_time']
        verbose_name = '用户'
        verbose_name_plural = '用户'

各字段含义:

  • name必填,最长不超越1叁13个字符,何况唯一,也正是不能有同等姓名;
  • password必填,最长不超过2伍十六个字符(实际可能无需如此长);
  • email使用Django内置的邮箱类型,并且独一;
  • 性别使用了二个choice,只好选取男还是女,默许为男;
  • 使用__str__帮扶人性化显示对象音讯;
  • 元数据里定义顾客按成立时间的反序排列,相当于近来的第一突显;

小心:这里的客商名指的是网络上登记的客商名,不要等同于现实中的真实姓名,所以利用了独一机制。假使是切实中能够另行的人名,那必然是不可能设置unique的。

 2.2.设置数据库为Mysql

在settings.py修改

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',        #数据库名字
        'USER': 'root',          #账号
        'PASSWORD': '123456',      #密码
        'HOST': '127.0.0.1',    #IP
        'PORT': '3306',                   #端口
    }
}

init.py里面导入pymysql模块

# login/init.py

import pymysql
pymysql.install_as_MySQLdb()

  2.3.数据库迁移

注册app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',
]

搬迁到数据库

python manage.py makemigrations

python manage.py migrate

三、admin后台

3.1.在admin中登记模型

# login/admin.py

from django.contrib import admin
from . import models

admin.site.register(models.User)

 3.2.创造顶级管理员

python manage.py createsuperuser

接下来再追加多少个测验客商

 

图片 2

 

 四、url路由和视图

 后边我们早已创办好数据模型了,何况在admin后新竹加多了有的测验顾客。上面大家就要统一准备好站点的url路由、对应的拍卖视图函数以及接纳的前端模板了。

 4.1.路由规划

起来设想须要上面包车型地铁四个UWranglerL:

 图片 3

虚构到登陆系统属于站点的拔尖作用,为了直观和更便于接受,这里未有利用二级路由的不二等秘书籍,而是在根路由下直接编写路由条款,一样也未尝采取反向解析名(name参数)。

# mysite_login/urls.py

from django.conf.urls import url
from django.contrib import admin
from login import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^login/', views.login),
    url(r'^register/', views.register),
    url(r'^logout/', views.logout),
]

4.2.架构最初视图

路由写好了,就进去login/views.py文本编写制定视图的框架,代码如下:

# login/views.py

from django.shortcuts import render,redirect

def index(request):
    pass
    return render(request,'login/index.html')

def login(request):
    pass
    return render(request,'login/login.html')

def register(request):
    pass
    return render(request,'login/register.html')

def logout(request):
    pass
    return redirect('/index/')

作者们先不心急达成视图内部的切实可行细节,而是把框架先搭建起来。

4.3.创设HTML页面文件

在类型根路径的login目录中制造贰个templates目录,再在templates目录里创制一个login目录

login/templates/login目录中开创多个文件index.htmllogin.html以及register.html
,并写入如下的代码:

index.html

{#login/templates/login/index.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>

login.html

{#login/templates/login/login.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>登录页面</h1>
</body>
</html>

register.html

{#login/templates/login/register.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<h1>注册页面</h1>
</body>
</html>

 五、前端页面设计

 5.1.原生HTML页面

login.html文本中的内容,写入上边包车型地铁代码:

{#login/templates/login/login.html#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
     <div style="margin: 15% 40%;">
        <h1>欢迎登录!</h1>
       <form action="/login/" method="post">
            <p>
                <label for="id_username">用户名:</label>
                <input type="text" id="id_username" name="username" placeholder="用户名" autofocus required />
            </p>

            <p>
                <label for="id_password">密码:</label>
                <input type="password" id="id_password" placeholder="密码" name="password" required >
            </p>
            <input type="submit" value="确定">
        </form>
    </div>



</body>
</html>

能够观望如下图的页面:

图片 4

5.2.引入Bootstrap

Bootstrap3.3.7下载地址

根目录下新建二个static目录,并将解压后的bootstrap-3.3.7-dist目录,全体拷贝到static目录中,如下图所示:

 图片 5

由于Bootstrap注重JQuery,所以大家需求超前下载并引进JQuery:下载地址

在static目录下,新建一个css和js目录,作为未来的体裁文件和js文件的寄存地,将大家的jquery文件拷贝到static/js目录下。

 图片 6

接下来展开项指标settings文件,在最上面增加配置,用于钦定静态文件的找寻目录:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

5.3.创建base.html模板

既是要将前端页面做得像个样子,那么就无法和最近同样,每一个页面都各写各的,单打独斗。多少个网址有谈得来的联合风格和公用部分,能够把那有的剧情聚集到二个基础模板base.html中。以后,在根目录下的templates中新建一个base.html文本用作站点的根基模板。

在Bootstrap文书档案中,为大家提供了三个特别轻便而又实用的大旨模板,代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>你好,世界!</h1>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
  </body>
</html>

将它完整拷贝到base.html文件中。

 5.4.创立页面导航条

Bootstrap提供了现存的导航条组件

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        Toggle navigation



      </button>
      <a class="navbar-brand" href="#">Brand</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">Link (current)</a></li>
        <li><a href="#">Link</a></li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown </a>
          <ul class="dropdown-menu">
            <li><a href="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">One more separated link</a></li>
          </ul>
        </li>
      </ul>
      <form class="navbar-form navbar-left">
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Search">
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
      </form>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#">Link</a></li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown </a>
          <ul class="dropdown-menu">
            <li><a href="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
          </ul>
        </li>
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

图片 7

中间有一部分有的,举个例子寻觅框是我们当下还无需的,必要将剩余的故事情节裁剪掉。同不经常候,有一点称谓和url地址等急需按我们的莫过于内容改换。最终导航条的代码如下:

<nav class="navbar navbar-default">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav" aria-expanded="false">
            切换导航条



          </button>
          <a class="navbar-brand" href="#">Mysite</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">主页</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="/login/">登录</a></li>
            <li><a href="/register/">注册</a></li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
    </nav>

5.5.选拔Bootstrap静态文件

{% static '相对路径' %}以此Django为我们提供的静态文件加载方法,能够将页面与静态文件链接起来

 最后,base.html剧情如下:

{% load staticfiles %}

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>{% block title %}base{% endblock %}</title>

    <!-- Bootstrap -->
    <link href="{%%20static%20'bootstrap-3.3.7-dist/css/bootstrap.min.css'%20%}" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
    {% block css %}{% endblock %}
  </head>
  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav" aria-expanded="false">
            切换导航条



          </button>
          <a class="navbar-brand" href="#">Mysite</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">主页</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="/login/">登录</a></li>
            <li><a href="/register/">注册</a></li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
    </nav>

    {% block content %}{% endblock %}


    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="{%%20static%20'js/jquery-3.2.1.js'%20%}"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="{%%20static%20'bootstrap-3.3.7-dist/js/bootstrap.min.js'%20%}"></script>
  </body>
</html>

简单易行表明:

  • 经过页面最上端的{% load staticfiles %}加载后,才方可选用static方法;
  • 通过{% block title %}base{% endblock %},设置了二个动态的页面title块;
  • 通过{% block css %}{% endblock %},设置了二个动态的css加载块;
  • 通过{% block content %}{% endblock %},为切实页面的重头戏内容留下接口;
  • 通过{%%20static%20'bootstrap-3.3.7-dist/css/bootstrap.min.css'%20%}将样式文件指向了我们的实际静态文件,下面包车型客车js脚本也是一样的道理。

 看下效果

图片 8

5.6.统一策画登陆页面

Bootstarp提供了贰个基本的表单样式,代码如下:

<form>
  <div class="form-group">
    <label for="exampleInputEmail1">Email address</label>
    <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
  </div>
  <div class="form-group">
    <label for="exampleInputPassword1">Password</label>
    <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
  </div>
  <div class="form-group">
    <label for="exampleInputFile">File input</label>
    <input type="file" id="exampleInputFile">
    <p class="help-block">Example block-level help text here.</p>
  </div>
  <div class="checkbox">
    <label>
      <input type="checkbox"> Check me out
    </label>
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>

如下:

图片 9

我们结合Bootstrap和后边本身写的form表单,修改login/templates/login/login.html成适合项目供给的榜样:

{% extends 'login/base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}
    <link rel="stylesheet" href="{%%20static%20'css/login.css'%20%}">
{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">
              <h2 class="text-center">欢迎登录</h2>
              <div class="form-group">
                <label for="id_username">用户名:</label>
                <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
              </div>
              <div class="form-group">
                <label for="id_password">密码:</label>
                <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
              </div>
              <button type="reset" class="btn btn-default pull-left">重置</button>
              <button type="submit" class="btn btn-primary pull-right">提交</button>
          </form>
        </div>
    </div> <!-- /container -->
{% endblock %}

说明:

  • 通过{% extends 'base.html' %}三番七回了‘base.html’模板的内容;
  • 通过{% block title %}登录{% endblock %}安装了特别的title;
  • 通过block css引进了针对的login.css亟待保留的都以种种客商的连带音信。体制文件;
  • 中心内容定义在block content内部
  • 加多了三个重新初始化按键。

static/css目录中新建二个login.css体制文件,这里差不离地写了点样式,

body {
  background-color: #eee;
}
.form-login {
  max-width: 330px;
  padding: 15px;
  margin: 0 auto;
}
.form-login .form-control {
  position: relative;
  height: auto;
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
  padding: 10px;
  font-size: 16px;
}
.form-login .form-control:focus {
  z-index: 2;
}
.form-login input[type="text"] {
  margin-bottom: -1px;
  border-bottom-right-radius: 0;
  border-bottom-left-radius: 0;
}
.form-login input[type="password"] {
  margin-bottom: 10px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

最后效果

图片 10

 六、登入视图

6.1.登陆视图

依照大家在路由中的设计,顾客通过login.html中的表单填写顾客名和密码,并以POST的不二秘技发送到服务器的/login/地址。服务器通过login/views.py中的login()视图函数,接收并拍卖这一呼吁。

咱俩得以因此下边包车型地铁秘籍接收和拍卖乞请:

def login(request):
    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')        return redirect('/index/')
    return render(request, 'login/login.html')

还必要在前端页面包车型地铁form表单内增多一个{% csrf_token %}标签:

<form class='form-login' action="/login/" method="post">
  {% csrf_token %}
  <h2 class="text-center">欢迎登录</h2>
  <div class="form-group">
  ......
</form>

进入登入页面,输入顾客名,密码然后跳转到index页面。

6.2.数量表明

通过唯一的客商名,使用Django的ORM去数据库中询问客户数据,假若有相称项,则实行密码相比,如果未有相配项,说明客商名不设有。假若密码比较错误,表明密码不科学。

def login(request):
    if request.method == "POST":
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        if username and password:  # 确保用户名和密码都不为空
            username = username.strip()
            # 用户名字符合法性验证
            # 密码长度验证
            # 更多的其它验证.....
            try:
                user = models.User.objects.get(name=username)
            except:
                return render(request, 'login/login.html')
            if user.password == password:
                return redirect('/index/')
    return render(request, 'login/login.html')

6.3.增加提醒信息

上边的代码还远远不足很关键的一部分剧情,提醒音讯!无论是登陆成功可能退步,顾客都未曾获得任何提醒消息,那鲜明是那二个的。

修改一下login视图:

def login(request):
    if request.method == "POST":
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        message = "所有字段都必须填写!"
        if username and password:  # 确保用户名和密码都不为空
            username = username.strip()
            # 用户名字符合法性验证
            # 密码长度验证
            # 更多的其它验证.....
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    return redirect('/index/')
                else:
                    message = "密码不正确!"
            except:
                message = "用户名不存在!"
        return render(request, 'login/login.html', {"message": message})
    return render(request, 'login/login.html')

扩张了message变量,用于保存提示消息。当有错误消息的时候,将错误音信打包成四个字典,然后作为第八个参数提要求render()方法。这一个数据字典在渲染模板的时候会传送到模板里供你调用。

为了在前面二个页面展现新闻,还索要对login.html张开退换:

{% extends 'login/base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}
    <link rel="stylesheet" href="{%%20static%20'css/login.css'%20%}">
{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
            <form class='form-login' action="/login/" method="post">

                {% if message %}
                    <div class="alert alert-warning">{{ message }}</div>
                {% endif %}

                {% csrf_token %}
                <h2 class="text-center">欢迎登录</h2>
                <div class="form-group">
                    <label for="id_username">用户名:</label>
                    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username"
                           autofocus required>
                </div>
                <div class="form-group">
                    <label for="id_password">密码:</label>
                    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password"
                           required>
                </div>
                <button type="reset" class="btn btn-default pull-left">重置</button>
                <button type="submit" class="btn btn-primary pull-right">提交</button>
            </form>
        </div>
    </div> <!-- /container -->
{% endblock %}

index.html主页模板也修改一下,删除原有内容,增添上面包车型客车代码:

{#login/templates/login/index.html#}

{% extends 'login/base.html' %}
{% block title %}主页{% endblock %}
{% block content %}
    <h1>欢迎回来!</h1>
{% endblock %}

 七、Django表单

亟待保留的都以种种客商的连带音信。Django的表单给我们提供了下边三个主要职能:

  • 准备和重构数据用于页面渲染;
  • 为数量创造HTML表单成分;
  • 收起和管理客商从表单发送过来的多少

 7.1.创立表单模型

from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="用户名", max_length=128)
    password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput)

说明:

  • 要伊始入forms模块
  • 享有的表单类都要持续forms.Form类
  • 各类表单字段都有谈得来的字段类型比方CharField,它们各自对应一种HTML语言中<form>内的贰个input成分。那或多或少和Django模型系统的统一准备丰裕相像。
  • 亟待保留的都以种种客商的连带音信。label参数用于安装<label>标签
  • max_length界定字段输入的最大尺寸。它同不经常候起到多少个功效,一是在浏览器页面限制客商输入不可超越字符数,二是在后端服务器验证客商输入的长短也不足超过。
  • widget=forms.PasswordInput用以内定该字段在form表单里表现为<input type='password' />,也便是密码输入框。

 7.2.修改视图

运用了Django的表单后,将在要视图中进行对应的更改:

# login/views.py

from django.shortcuts import render,redirect
from . import models
from .forms import UserForm

def index(request):
    pass
    return render(request,'login/index.html')

def login(request):
    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "请检查填写的内容!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    return redirect('/index/')
                else:
                    message = "密码不正确!"
            except:
                message = "用户不存在!"
        return render(request, 'login/login.html', locals())

    login_form = UserForm()
    return render(request, 'login/login.html', locals())

说明:

  • 对于非POST方法发送数据时,比方GET方法央求页面,返回空的表单,让客商能够填入数据;
  • 对于POST方法,接收表单数据,并表明;
  • 选拔表单类自带的is_valid()亟待保留的都以种种客商的连带音信。措施一步成功数据证实职业;
  • 表明成功后能够从表单对象的cleaned_data多少字典中收获表单的具体值;
  • 倘使评释不通过,则赶回二个暗含先前数据的表单给前端页面,方便客商修改。约等于说,它会帮你保存以前填写的多寡内容,实际不是回去一个空表!

除此以外,这里运用了八个小能力,Python内置了三个locals()函数,它回到当前持有的地面变量字典,大家得以偷懒的将那作为render函数的数据字典参数值,就不要费力去组织二个形如{'message':message, 'login_form':login_form}亟待保留的都以种种客商的连带音信。的字典了。这样做的实惠当然是大大有助于了大家,然而同期也或然往模板传入了某些余下的变量数据,产生数据冗余收缩功用。

7.3.修改login界面

Django的表单很主要的一个效应正是自动生成HTML的form表单内容。未来,大家须求修改一下原先的login.html文件:

{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}<link href="{%%20static%20'css/login.css'%20%}" rel="stylesheet"/>{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}
              {% csrf_token %}
              <h2 class="text-center">欢迎登录</h2>

              {{ login_form }}

              <button type="reset" class="btn btn-default pull-left">重置</button>
              <button type="submit" class="btn btn-primary pull-right">提交</button>

          </form>
        </div>
    </div> <!-- /container -->
{% endblock %}

浏览器生成的HTML源码

再也启航服务器,刷新页面,如下图所示:图片 11

图片 12

亟待保留的都以种种客商的连带音信。7.4.手动渲染表单

直接{{ login_form }}就算好,啥都毫不顾虑,可是分界面真的比极不好看,往往并不是您想要的,倘使你要选拔CSS和JS,比如你要引进Bootstarps框架,这么些都亟需对表单内的input成分进行额外调控,那怎么做呢?手动渲染字段就足以了。

能够经过{{ login_form.name_of_field }}赢得每贰个字段,然后分别渲染,如下例所示:

<div class="form-group">
  {{ login_form.username.label_tag }}
  {{ login_form.username}}
</div>
<div class="form-group">
  {{ login_form.password.label_tag }}
  {{ login_form.password }}
</div>

下一场,在form类里丰盛attr属性就能够,如下所示修改login/forms.py

from django import forms

class UserForm(forms.Form):
    username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))

重复刷新页面,就显得平常了!

 八、图片验证码

为了防御机器人频仍登入网址还是破坏分子恶意登入,比非常多顾客登入和注册系统都提供了图片验证码作用。

验证码(CAPTCHA)是“Completely Automated Public Turing test to tell
计算机s and Humans
Apart”(全自动区分Computer和人类的图灵测验)的缩写,是一种有别于客户是Computer如故人的公共机关程序。可防止范恶意破解密码、刷票、论坛灌水,有效防护有些骇客对某一个一定注册客户用特定程序暴力破解情势实行持续的登入尝试。

图片验证码的历史相比遥远,到现行反革命一度有些英雄末路的含意了。因为机器学习、图像识别的存在,机器人已经得以相比较科学的鉴定识别图像内的字符了。但不管怎么说,作为一种防范花招,至少依然得以抵抗一些起码入门的攻击花招,抬高了攻击者的秘籍。

在Django中完毕图片验证码功能特别轻易,有现存的第三方库能够利用,大家不要自身开辟(也要能开采得出去,囧)。这一个库叫做django-simple-captcha

8.1.安装captcha

直白设置:pip install
django-simple-captcha

Django自动帮大家设置了连带的重视库sixolefilePillow,个中的Pillow是红得发紫的绘图模块。

注册captcha

在settings中,将‘captcha’注册到app列表里:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',
    'captcha',
]

captcha要求在数据库中树立和谐的数据表,所以须要施行migrate命令生成数据表:

python manage.py migrate

8.2.添加url路由

根目录下的urls.py文件中加进captcha对应的网站:

from django.conf.urls import url
from django.conf.urls import include
from django.contrib import admin
from login import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^login/', views.login),
    url(r'^register/', views.register),
    url(r'^logout/', views.logout),
    url(r'^captcha', include('captcha.urls'))  # 增加这一行
]

8.3.修改forms.py

假若位置都OK了,就能够直接在大家的forms.py文件中增添Captcha菲尔德了。

from django import forms
from captcha.fields import CaptchaField

class UserForm(forms.Form):
    username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    captcha = CaptchaField(label='验证码')

急需超前导入from captcha.fields import CaptchaField,然后就疑似写普通的form字段一样增添一个captcha字段就可以了!

 8.4.修改login.html

 由于大家前面是手动生成的form表单,所以还要修改一下,加多captcha的相干内容,如下所示:

{% extends 'login/base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}
    <link rel="stylesheet" href="{%%20static%20'css/login.css'%20%}">
{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}
              {% csrf_token %}
              <h2 class="text-center">欢迎登录</h2>
              <div class="form-group">
                  {{ login_form.username.label_tag }}
                  {{ login_form.username}}
              </div>
              <div class="form-group">
                  {{ login_form.password.label_tag }}
                  {{ login_form.password }}
              </div>

              <div class="form-group">
                  {{ login_form.captcha.errors }}
                  {{ login_form.captcha.label_tag }}
                  {{ login_form.captcha }}
              </div>

              <button type="reset" class="btn btn-default pull-left">重置</button>
              <button type="submit" class="btn btn-primary pull-right">提交</button>

          </form>
        </div>
    </div> <!-- /container -->
{% endblock %}

此间额外扩张了一条{{ login_form.captcha.errors }}用来明显提醒客户,你的验证码不科学

 查阅效果:

图片 13

内部验证图形码是不是科学的办事都以在后台自动实现的,只须要动用is_valid()本条forms内置的注明格局就三头打开了,完全无需在视图函数中充足任何的验证代码,特别方便飞快!

 九、session会话

       
因为因特网HTTP合同的性状,每叁遍来自于客商浏览器的乞求(request)都是无状态的、独立的。通俗地说,正是无力回天保存客商处境,后台服务器根本就不明白当前恳请和以前及事后央求是还是不是来自同一客商。对于静态网站,那只怕不是个难题,而对此动态网址,尤其是京东、天猫商城、银行等购物或经济网址,无法识别客户并保持客商意况是沉重的,根本就无法提供服务。你可以品尝将浏览器的cookie作用关闭,你会意识将不或者在京东报到和购物。

为了落到实处连接情形的涵养功能,网址会经过客户的浏览器在客商机器内被限制的硬盘地方中写入一些数量,也正是所谓的Cookie。通过Cookie能够保留一些诸如客商名、浏览记录、表单记录、登入和撤回等各样数据。然则这种艺术充裕不安全,因为Cookie保存在客户的机器上,要是Cookie被仿制假冒、篡改或删除,就能导致巨大的安全威吓,因而,今世网址设计一般将Cookie用来保存一些不重大的源委,实际的客商数量和情景照旧以Session会话的措施保留在劳动器端。

Session依赖库克ie!但与Cookie区别的地点在于Session将富有的多寡都投身服务器端,客户浏览器的Cookie中只会保留一个非明文的辨识音信,比如哈希值。

Django提供了八个通用的Session框架,並且能够应用多种session数据的保存方法:

  • 保留在数据库内
  • 封存到缓存
  • 保存到文件内
  • 保存到cookie内

平日状态,未有刻意须要的话,请使用保留在数据库内的办法,尽量不要保存到Cookie内。

Django的session框架暗中同意启用,并已经登记在app设置内,假设确实未有启用,那么参谋下边包车型客车源委增添有表达的这两行,再实践migrate命令成立数据表,就足以采纳session了。

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',    # 这一行
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',  # 这一行
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

当session启用后,传递给视图request参数的HttpRequest对象将满含贰个session属性,就像是贰个字典对象同样。你能够在Django的别样地点读写request.session天性,可能反复编纂使用它。

上边是session使用参谋:

class backends.base.SessionBase
        # 这是所有会话对象的基类,包含标准的字典方法:
        __getitem__(key)
            Example: fav_color = request.session['fav_color']
        __setitem__(key, value)
            Example: request.session['fav_color'] = 'blue'
        __delitem__(key)
            Example: del request.session['fav_color']  # 如果不存在会抛出异常
        __contains__(key)
            Example: 'fav_color' in request.session
        get(key, default=None)
            Example: fav_color = request.session.get('fav_color', 'red')
        pop(key, default=__not_given)
            Example: fav_color = request.session.pop('fav_color', 'blue')

 # 类似字典数据类型的内置方法
        keys()
        items()
        setdefault()
        clear()


        # 它还有下面的方法:
        flush()
            # 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。

        set_test_cookie()
            # 设置一个测试cookie,用于探测用户浏览器是否支持cookies。由于cookie的工作机制,你只有在下次用户请求的时候才可以测试。
        test_cookie_worked()
            # 返回True或者False,取决于用户的浏览器是否接受测试cookie。你必须在之前先调用set_test_cookie()方法。
        delete_test_cookie()
            # 删除测试cookie。
        set_expiry(value)
            # 设置cookie的有效期。可以传递不同类型的参数值:
        • 如果值是一个整数,session将在对应的秒数后失效。例如request.session.set_expiry(300) 将在300秒后失效.
        • 如果值是一个datetime或者timedelta对象, 会话将在指定的日期失效
        • 如果为0,在用户关闭浏览器后失效
        • 如果为None,则将使用全局会话失效策略
        失效时间从上一次会话被修改的时刻开始计时。

        get_expiry_age()
            # 返回多少秒后失效的秒数。对于没有自定义失效时间的会话,这等同于SESSION_COOKIE_AGE.
            # 这个方法接受2个可选的关键字参数
        • modification:会话的最后修改时间(datetime对象)。默认是当前时间。
        •expiry: 会话失效信息,可以是datetime对象,也可以是int或None

        get_expiry_date()
            # 和上面的方法类似,只是返回的是日期

        get_expire_at_browser_close()
            # 返回True或False,根据用户会话是否是浏览器关闭后就结束。

        clear_expired()
            # 删除已经失效的会话数据。
        cycle_key()
            # 创建一个新的会话秘钥用于保持当前的会话数据。django.contrib.auth.login() 会调用这个方法。

9.1.使用session

首先,修改login/views.py中的login()视图函数:

def login(request):
    if request.session.get('is_login',None):
        return redirect('/index')

    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "请检查填写的内容!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.name
                    return redirect('/index/')
                else:
                    message = "密码不正确!"
            except:
                message = "用户不存在!"
        return render(request, 'login/login.html', locals())

    login_form = UserForm()
    return render(request, 'login/login.html', locals())

因而上边的if语句,大家差别意再次登陆:

if request.session.get('is_login',None):
    return redirect("/index/")

透过下边的话语,大家往session字典内写入客户情形和数目:

request.session['is_login'] = True
request.session['user_id'] = user.id
request.session['user_name'] = user.name

你完全可现在里面写任何数据,不止限于客商相关!

既然有了session记录客户登录状态,那么就足以健全大家的登出视图函数了:

def logout(request):
    if not request.session.get('is_login', None):
        # 如果本来就未登录,也就没有登出一说
        return redirect("/index/")
    request.session.flush()
    # 或者使用下面的方法
    # del request.session['is_login']
    # del request.session['user_id']
    # del request.session['user_name']
    return redirect("/index/")

flush()方法是相比较安全的一种做法,而且贰次性将session中的全部内容总体清空,确认保证不留后患。但也许有倒霉的地点,那就是只要你在session中夹带了有些‘走私货物’,会被一并剔除,那点应当要稳重。

 9.2.完善页面

有了客户情形,就足以根据客商登入与否,呈现差异的页面,举例导航条内容:

首先,修改base.html文件:

 <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">主页</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
              {% if request.session.is_login %}
                  <li><a href="#">当前在线:{{ request.session.user_name }}</a></li>
                  <li><a href="/logout/">登出</a></li>
              {% else %}
                  <li><a href="/login/">登录</a></li>
                  <li><a href="/register/">注册</a></li>
              {% endif %}
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->

通过if判别,当登陆时,呈现当前客户名和发布按键。未登陆时,彰显登入和登记按键。

细心个中的模版语言,{{ request }}本条变量会被暗许传入模板中,能够通过圆点的调用格局,获取它里面包车型大巴{{ request.session }},再进一步的获取session中的内容。其实{{ request }}中的数据远不仅仅此,比如{{ request.path }}就足以获得先前的url地址。

再修改一下index.html页面,依据登陆与否的例外,呈现不相同的剧情:

{% extends 'base.html' %}
{% block title %}主页{% endblock %}
{% block content %}
    {% if request.session.is_login %}
    <h1>你好,{{ request.session.user_name }}!欢迎回来!</h1>
    {% else %}
    <h1>你尚未登录,只能访问公开内容!</h1>
    {% endif %}
{% endblock %}

看下效果:

图片 14

图片 15

 

 十、注册视图

 10.1.创建forms

/login/forms.py中加多三个新的表单类:

class RegisterForm(forms.Form):
    gender = (
        ('male', "男"),
        ('female', "女"),
    )
    username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password1 = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    password2 = forms.CharField(label="确认密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label="邮箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
    sex = forms.ChoiceField(label='性别', choices=gender)
    captcha = CaptchaField(label='验证码')

说明:

  • gender和User模型中的同样,其实能够拉出来作为常量共用,为了直观,特意重写壹次;
  • password1和password2,用于输入四回密码,并展开相比较,防止误输密码;
  • email是贰个邮箱输入框;
  • sex是一个select下拉框;

 10.2.完善register.html

同样地,类似login.html文件,我们在register.html中编辑forms相关条约:

{% extends 'login/base.html' %}

{% block title %}注册{% endblock %}
{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-register' action="/register/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}

              {% csrf_token %}

              <h2 class="text-center">欢迎注册</h2>
              <div class="form-group">
                  {{ register_form.username.label_tag }}
                  {{ register_form.username}}
              </div>
              <div class="form-group">
                  {{ register_form.password1.label_tag }}
                  {{ register_form.password1 }}
              </div>
              <div class="form-group">
                  {{ register_form.password2.label_tag }}
                  {{ register_form.password2 }}
              </div>
              <div class="form-group">
                  {{ register_form.email.label_tag }}
                  {{ register_form.email }}
              </div>
              <div class="form-group">
                  {{ register_form.sex.label_tag }}
                  {{ register_form.sex }}
              </div>
              <div class="form-group">
                  {{ register_form.captcha.errors }}
                  {{ register_form.captcha.label_tag }}
                  {{ register_form.captcha }}
              </div>

              <button type="reset" class="btn btn-default pull-left">重置</button>
              <button type="submit" class="btn btn-primary pull-right">提交</button>

          </form>
        </div>
    </div> <!-- /container -->
{% endblock %}

10.3.报了名视图

进入/login/views.py文本,今后来完善大家的register()视图:

def register(request):
    if request.session.get('is_login', None):
        # 登录状态不允许注册。你可以修改这条原则!
        return redirect("/index/")
    if request.method == "POST":
        register_form = RegisterForm(request.POST)
        message = "请检查填写的内容!"
        if register_form.is_valid():  # 获取数据
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2:  # 判断两次密码是否相同
                message = "两次输入的密码不同!"
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:  # 用户名唯一
                    message = '用户已经存在,请重新选择用户名!'
                    return render(request, 'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:  # 邮箱地址唯一
                    message = '该邮箱地址已被注册,请使用别的邮箱!'
                    return render(request, 'login/register.html', locals())

                # 当一切都OK的情况下,创建新用户

                new_user = models.User.objects.create()
                new_user.name = username
                new_user.password = password1
                new_user.email = email
                new_user.sex = sex
                new_user.save()
                return redirect('/login/')  # 自动跳转到登录页面
    register_form = RegisterForm()
    return render(request, 'login/register.html', locals())

从大要上逻辑上,也是先实例化四个RegisterForm的目的,然后选择is_valide()证实数据,再从cleaned_data中获取数据。

最首要在于注册逻辑,首先五回输入的密码必需一致,其次无法存在同样客商名和信箱,最终只要基准都满意,利用ORM的API,创制贰个客商实例,然后保留到数据库内。

看一下报了名的页面:

图片 16

登记成功在admin后台能够见到登记的顾客

图片 17

10.4.密码加密

客户注册的密码应该加密才对

对此怎么样加密密码,有成都百货上千不一的门径,其安全程度也高低不等。这里我们利用Python内置的hashlib库,使用哈希值的点子加密密码,或者安全品级相当矮,但丰硕轻易,方便使用,不是么?

首先在login/views.py中编辑贰个hash函数:

import hashlib

def hash_code(s, salt='mysite'):# 加点盐
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())  # update方法只接收bytes类型
    return h.hexdigest()

下一场,咱们还要对login()和register()视图实行一下改变:

#login.html

if user.password == hash_code(password):  # 哈希值和数据库内的值进行比对

#register.html

new_user.password = hash_code(password1)  # 使用加密密码

图片 18图片 19

# login/views.py

from django.shortcuts import render,redirect
from . import models
from .forms import UserForm,RegisterForm
import hashlib

def index(request):
    pass
    return render(request,'login/index.html')

def login(request):
    if request.session.get('is_login', None):
        return redirect("/index/")
    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "请检查填写的内容!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(name=username)
                if user.password == hash_code(password):  # 哈希值和数据库内的值进行比对
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.name
                    return redirect('/index/')
                else:
                    message = "密码不正确!"
            except:
                message = "用户不存在!"
        return render(request, 'login/login.html', locals())

    login_form = UserForm()
    return render(request, 'login/login.html', locals())


def register(request):
    if request.session.get('is_login', None):
        # 登录状态不允许注册。你可以修改这条原则!
        return redirect("/index/")
    if request.method == "POST":
        register_form = RegisterForm(request.POST)
        message = "请检查填写的内容!"
        if register_form.is_valid():  # 获取数据
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2:  # 判断两次密码是否相同
                message = "两次输入的密码不同!"
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:  # 用户名唯一
                    message = '用户已经存在,请重新选择用户名!'
                    return render(request, 'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:  # 邮箱地址唯一
                    message = '该邮箱地址已被注册,请使用别的邮箱!'
                    return render(request, 'login/register.html', locals())

                # 当一切都OK的情况下,创建新用户

                new_user = models.User.objects.create()
                new_user.name = username
                new_user.password = hash_code(password1)  # 使用加密密码
                new_user.email = email
                new_user.sex = sex
                new_user.save()
                return redirect('/login/')  # 自动跳转到登录页面
    register_form = RegisterForm()
    return render(request, 'login/register.html', locals())

def logout(request):
    if not request.session.get('is_login',None):
        return redirect('/index/')
    request.session.flush()

    return redirect('/index/')

def hash_code(s, salt='mysite_login'):
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())  # update方法只接收bytes类型
    return h.hexdigest()

views.py全体代码

重启服务器,步向注册页面,新建贰个客商,然后进入admin后台,查看客户的密码情状:

图片 20

 再利用该客商登陆一下,马到成功!

能够看来密码长度依据你哈希算法的不如,已经变得相当短了,所以往边model中装置password字段时,不要想当然的将max_length安装为16这么小的数字。

 

相关文章