python(十五)--Django实例以及原理剖析

Django实例以及原理剖析

  • 1.基于Django3的可重用注册登陆系统
  • 2 项目开始前的思考
  • 3 搭建项目环境(第一步)
    • 3.1 创建Django项目loginRegister
    • 3.2 创建app
    • 3.3 创建时区和语言
    • 3.4 数据库表生成
    • 3.5 启动开发服务器
    • 3.6 浏览器访问,检测是否成功?(第一步完美搞定)
    • 3.7 git提交项目代码到本地仓库
      • 3.7.1 检测git是否安装成功
      • 3.7.2 安装.ignore插件
      • 3.7.3 忽略提交的代码
      • 3.7.2 git提交代码
  • 4. 设计数据库模型(第二步)
    • 4.1 设计过程
    • 4.2 模型代码
    • 4.4 设置数据库后端
    • 4.5 注册app
    • 4.6 生成迁移脚本并写入数据库
    • 4.7 测试是否成功
      • 4.7.1 测试数据库信息
      • 4.7.2 命令测试并查看网页更新内容
    • 4.8 数据库模型后台管理
    • 4.9 git 提交
  • 5. 路由与视图函数框架搭建
    • 5.1 路由设计
    • 5.2 访问策略
    • 5.3 路由配置
      • 5.3.1 主路由配置文件
      • 5.3.2 子路由配置文件(login子应用的)
      • 5.3.3 视图函数的配置
      • 5.3.4 模板template的配置
      • 5.3.5 测试是否成功
      • 5.3.6 git 提交
  • 6. 前端界面设计与优化
    • 6.1 模板参考官网
    • 6.2 代码
    • 6.3 git提交
    • 6.4 效果
  • 7. 完善登陆的视图函数(业务逻辑)
    • 7.1 html和视图函数交互的完善
      • 7.1.1 HTML的完善
      • 7.1.2 视图函数的完善(login)
      • 7.1.3 测试是否成功
      • 7.1.4 git提交
  • 8.session会话和登录的视图函数
    • 8.1 登陆成功,存储登录的用户信息到session中
      • 8.1.1 登录时
      • 8.1.2 登出时,清空session信息
      • 8.1.3 添加登出的超链接
      • 8.1.4 测试(index)
      • 8.1.5 git提交
  • 9.上传到giee
    • 9.1 新建仓库
    • 9.2 上传到giie仓库准备工作
      • 9.2.1 创建README.md文件
      • 9.2.2 生成requirements.txt文件(所需的包)
      • 9.2.3 绑定gitee仓库
      • 9.2.4 git提交
    • 9.3 上传仓库
  • 10.查看之前的修改内容
    • 10.1 从gitee仓库查看
    • 10.2 命令行方式
  • 11. 图片验证码(captcha)
    • 11.1 第三方库的设置(验证码插件)
    • 11.2 git提交(验证码插件的设置)
    • 11.3 Django表单
      • 11.3.1 创建表单模型
      • 11.3.2 视图逻辑优化
      • 11.3.3 Template页面优化
      • 11.3.4 验证是否正确
      • 11.3.5 git 提交
  • 13. 邮箱注册
    • 13.1 获取授权码并开启SMTP
    • 13.2 配置邮件信息
    • 13.3 交互式环境中测试发送邮件是否成功?
    • 13.4 注册表单
    • 13.4 实现注册视图
    • 13.5 template的修改
    • 13.6 测试是否成功
    • 13.7 注册添加密码加密功能
    • 13.8 邮箱注册确认(是否是真正的邮箱)
      • 13.8.1 创建模型
      • 13.8.2 修改视图
      • 13.8.3 邮件相关的设置
      • 13.8.4 测试
      • 13.8.5 处理邮件确认请求
      • 13.8.6 修改登录规则
      • 13.8.7 测试
  • 14. 其他

1.基于Django3的可重用注册登陆系统

注册(邮箱(本次实验核心),手机,微信,qq)
登录
注销

2 项目开始前的思考

#路由配置
urlpattern = [
	path('/register/', views.register),
	path('/login/', views.login),
	path('/logout/', views.logout),
]
#视图配置(重点)
#数据库模型配置Model(id,
class User:
	id, name, password, email, create_time, update_time,last_time(最后一次登录的时间),gender, province
#模板Template: register.html,login.html,index.html

3 搭建项目环境(第一步)

3.1 创建Django项目loginRegister

python(十五)--Django实例以及原理剖析_第1张图片

3.2 创建app

python manage.py startapp login

在这里插入图片描述

3.3 创建时区和语言

# filename: loginRegister/settings.py
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

python(十五)--Django实例以及原理剖析_第2张图片

3.4 数据库表生成

 python manage.py makemigrations   ##生成迁移脚本
 python manage.py migrate # 将迁移脚本的内容写入数据库并创建数据库表
 python manage.py createsuperuser # 创建后台登录的超级用户

python(十五)--Django实例以及原理剖析_第3张图片

python(十五)--Django实例以及原理剖析_第4张图片

3.5 启动开发服务器

方法一: 命令启动

python manage.py runserver 9999   ##端口默认8000,可以指定

python(十五)--Django实例以及原理剖析_第5张图片

方法二: 配置Django Server

python(十五)--Django实例以及原理剖析_第6张图片

python(十五)--Django实例以及原理剖析_第7张图片

3.6 浏览器访问,检测是否成功?(第一步完美搞定)

访问网址: http://127.0.0.1:8000
访问网址: http://127.0.0.1:8000/admin
python(十五)--Django实例以及原理剖析_第8张图片

python(十五)--Django实例以及原理剖析_第9张图片

python(十五)--Django实例以及原理剖析_第10张图片

3.7 git提交项目代码到本地仓库

3.7.1 检测git是否安装成功

- 没有添加成功,可以重新安装,安装时,点击添加环境变量。

python(十五)--Django实例以及原理剖析_第11张图片

3.7.2 安装.ignore插件

python(十五)--Django实例以及原理剖析_第12张图片

3.7.3 忽略提交的代码

python(十五)--Django实例以及原理剖析_第13张图片

python(十五)--Django实例以及原理剖析_第14张图片
python(十五)--Django实例以及原理剖析_第15张图片

3.7.2 git提交代码

git init
# 安装插件.ignore, 并生成python上传git项目需要忽略内容的文件.gitignore
git add * # 添加修改到暂存区
git commit -m "搭建项目开发环境" # 将暂存区的代码提交到本地git仓库
git log # 查看历史提交记录

python(十五)--Django实例以及原理剖析_第16张图片
如果提交写的中文,查看日志出现乱码可是尝试下面方法!
python(十五)--Django实例以及原理剖析_第17张图片

4. 设计数据库模型(第二步)

4.1 设计过程

- 作为一个用户登录和注册项目,需要保存的都是各种用户的相关信息。很显然,我们至少需要一张用户
	表User,在用户表里需要保存下面的信息:
	用户名(name): 必填,最长不超过128个字符且唯一(unique)
	密码(password): 必填,最长不超过256个字符
	邮箱地址(email): 使用Django内置的邮箱类型且唯一
	性别(gender): 性别, 使用choice,只能选择男或者女或者未知,默认为未知;
	创建时间(create_time): 用户创建时间
		注意点: auto_now_add=True时为添加时的时间,更新对象时不会有变动。
	修改时间(modify_time):用户最后一次修改时间
		注意点: auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。
	最后一次登录时间(last_login_time): 最后一次登录时间
		注意点:null=True的话,数据库中该字段是NULL,即允许空值
		注意点:blank=False(默认)的话,字段没被赋值则会抛错;和数据验证(表单验证等)有关.

4.2 模型代码

# login/models.py
from django.db import models
# Create your models here.
# appname_siteuser
class SiteUser(models.Model):
	"""用户的数据库模型,注册/登录需要"""
	gender_choice = (
	(0, "未知"),
	(1, "男"),
	(2, "女"),
	)
	name = models.CharField(max_length=128, unique=True, verbose_name="用户名")
	password = models.CharField(max_length=256, verbose_name="密码")
	email = models.EmailField(unique=True, verbose_name="电子邮箱")
	gender = models.IntegerField(choices=gender_choice, default=0,verbose_name="性别")
	# auto_now_add=True时为添加时的时间,更新对象时不会有变动。
	# auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。
	create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
	modify_time = models.DateTimeField(auto_now=True, verbose_name="最后一次修改时间")
	# null针对数据库层面的, blank针对表单的
	last_login_time = models.DateTimeField(null=True, blank=True,verbose_name="最后一次登录时间")
	
	def __str__(self):
		return self.name    ##为了显示美观
	class Meta:
		verbose_name = "网站用户管理"
		verbose_name_plural = verbose_name

python(十五)--Django实例以及原理剖析_第18张图片

4.4 设置数据库后端

Django支持MySQL, Sqlite, oracle等数据库, 此处选择默认的sqlite,不做修改。

4.5 注册app

# loginRegister/settings.py
INSTALLED_APPS = [
	'django.contrib.admin',
	'django.contrib.auth',
	'django.contrib.contenttypes',
	'django.contrib.sessions',
	'django.contrib.messages',
	'django.contrib.staticfiles',
	'login', # 修改的内容
]

python(十五)--Django实例以及原理剖析_第19张图片

4.6 生成迁移脚本并写入数据库

$ python manage.py makemigrations   ##生成迁移脚本
$ python manage.py migrate # 将迁移脚本的内容写入数据库并创建数据库表
$ python manage.py createsuperuser # 创建后台登录的超级用户

python(十五)--Django实例以及原理剖析_第20张图片

4.7 测试是否成功

4.7.1 测试数据库信息

  • 打开数据库文件db.sqlite3, 查看是否有数据库表login_siteuser,如果有,则操作成功。

python(十五)--Django实例以及原理剖析_第21张图片

python(十五)--Django实例以及原理剖析_第22张图片

python(十五)--Django实例以及原理剖析_第23张图片

4.7.2 命令测试并查看网页更新内容

python(十五)--Django实例以及原理剖析_第24张图片
python(十五)--Django实例以及原理剖析_第25张图片
python(十五)--Django实例以及原理剖析_第26张图片
python(十五)--Django实例以及原理剖析_第27张图片

4.8 数据库模型后台管理

#alt+回车自动导入模块
#login/admin.py
from django.contrib import admin
# Register your models here.
##后台管理设置信息
from login.models import SiteUser
class SiteUserAdmin(admin.ModelAdmin):
    list_display = ['name','gender','email']
    list_display_links = ['name']   ##可以点击进入的
    list_filter = ['gender','create_time']
    list_per_page = 10
admin.site.register(SiteUser,SiteUserAdmin)  ##将后台和模型关联
  • 测试,访问网址127.0.0.1:8000/admin
    python(十五)--Django实例以及原理剖析_第28张图片
    python(十五)--Django实例以及原理剖析_第29张图片

4.9 git 提交

git add *
git commit -m "add SiteUser admin management"
git log

python(十五)--Django实例以及原理剖析_第30张图片

5. 路由与视图函数框架搭建

5.1 路由设计

python(十五)--Django实例以及原理剖析_第31张图片

5.2 访问策略

未登录人员,不论是访问index还是login和logout,全部跳转到login界面
已登录人员,访问login会自动跳转到index页面
已登录人员,不允许直接访问register页面,需先logout
登出后,自动跳转到login界面

5.3 路由配置

5.3.1 主路由配置文件


#lgoinRegister/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('login.urls'))  #添加的行,如果没有前缀,访问子路由配置文件
]

python(十五)--Django实例以及原理剖析_第32张图片

5.3.2 子路由配置文件(login子应用的)

#login/urls.py(新建的)
from django.contrib import admin
from django.urls import path, include
from login import views
urlpatterns = [
    path('index/', views.index, name='index'),    ##导入url,并关联view
    path('login/', views.login, name='login'),
    path('register/', views.register, name='register'),
    path('logout/', views.logout, name='logout'),
]

python(十五)--Django实例以及原理剖析_第33张图片

5.3.3 视图函数的配置

#login/views.py
from django.shortcuts import render, redirect
# Create your views here.
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
    # redirect: 重定向(跳转),
    return redirect('/login/')

python(十五)--Django实例以及原理剖析_第34张图片

5.3.4 模板template的配置

#templates/login/index.html(新建)

<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>首页title>
head>
<body>
<h1>这是首页的模拟界面h1>
body>
html>


#templates/login/login.html(新建)

<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>用户登录title>
head>
<body>
<h1>用户登录h1>
<form>
	用户名: <input type="text" placeholder="username"><br/>
	密码: <input type="password" placeholder="password"><br/>
	<input type="submit" value="登录">
form>
body>
html>


#templates/login/register.html(新建)

<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>注册界面title>
head>
<body>
<h1>用户注册h1>
<form>
	用户名: <input type="text" placeholder="username"><br/>
	电子邮箱: <input type="email" placeholder="email"><br/>
	密码: <input type="password" placeholder="password"><br/>
	确认密码: <input type="password" placeholder="password"><br/>
	<input type="submit" value="注册">
form>
body>
html>

python(十五)--Django实例以及原理剖析_第35张图片

5.3.5 测试是否成功

- 	访问网址: http://127.0.0.1:9999/index/
	访问网址: http://127.0.0.1:9999/login/
	访问网址: http://127.0.0.1:9999/register/

python(十五)--Django实例以及原理剖析_第36张图片
python(十五)--Django实例以及原理剖析_第37张图片
python(十五)--Django实例以及原理剖析_第38张图片

5.3.6 git 提交

git add *
git commit -m "add init url/view/template"

python(十五)--Django实例以及原理剖析_第39张图片

6. 前端界面设计与优化

6.1 模板参考官网

  • 在颜值即正义的年代,但没有CSS和JS,样子真的令人无法接受。
    然而,大多数使用Django的人都不具备多高的前端水平,通常也没有专业的前端工程师配合,自己写的
    CSS和JS却又往往惨不忍睹。怎么办?没关系,我们有现成的开源前端CSS框架!Bootstrap4就是最好
    的CSS框架之一!戳一下了解更多
    Bootstrap核心汇总:
    入门模板
    栅格系统
    表单组件
    警告框

6.2 代码

##index.html

<html lang="zh-CN">
  <head>
    
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">

    <title>首页title>
  head>
  <body>
    <h1>这是首页的模拟界面h1>

    

    
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous">script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV" crossorigin="anonymous">script>

    
    
  body>
html>

##login.html

<html lang="zh-CN">
<head>
    
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
          integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">

    <title>用户登录title>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-sm">

        div>
        <div class="col-sm">
            <h3 style="text-align: center">用户登录h3>
            <div class="alert alert-warning" role="alert">
                <strong>登录失败!strong> 用户密码错误!
            div>

            <form>
                <div class="form-group">
                    <label>用户名label>
                    <input type="text" class="form-control">
                div>
                <div class="form-group">
                    <label>Passwordlabel>
                    <input type="password" class="form-control">
                    <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.small>
                div>
                <a href="/register/" class="text-success">
                    <ins>新用户注册ins>
                a>
                <button type="submit" class="btn btn-primary float-right">登录button>
            form>

        div>
        <div class="col-sm">

        div>
    div>
div>



<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js"
        integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
        crossorigin="anonymous">script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV"
        crossorigin="anonymous">script>



body>
html>

##register.html

<html lang="zh-CN">
<head>
    
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
          integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">

    <title>注册界面title>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-sm">

        div>
        <div class="col-sm">
            <h3 style="text-align: center">用户注册h3>
            <form>
                <div class="form-group">
                    <label>用户名label>
                    <input type="text" class="form-control">
                div>
                <div class="form-group">
                    <label>电子邮箱label>
                    <input type="email" class="form-control">
                div>
                <div class="form-group">
                    <label>密码label>
                    <input type="password" class="form-control">
                    <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.small>
                div>
                <div class="form-group">
                    <label>确认密码label>
                    <input type="password" class="form-control">
                    <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.small>
                div>
                <a href="/login/" class="text-success">
                    <ins>用户登录ins>
                a>
                <button type="submit" class="btn btn-primary float-right">注册button>
            form>

        div>
        <div class="col-sm">

        div>
    div>
div>



<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js"
        integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
        crossorigin="anonymous">script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV"
        crossorigin="anonymous">script>



body>
html>

6.3 git提交

python(十五)--Django实例以及原理剖析_第40张图片

6.4 效果

python(十五)--Django实例以及原理剖析_第41张图片

python(十五)--Django实例以及原理剖析_第42张图片

7. 完善登陆的视图函数(业务逻辑)

7.1 html和视图函数交互的完善

7.1.1 HTML的完善

  • 错误原因是CSRF验证失败,请求被中断。CSRF(Cross-site request forgery)跨站请求伪造,是一种常见的网络攻击手段,具体原理和技术内容请自行百科。Django自带对许多常见攻击手段的防御机制,CSRF就是其中一种,还有XSS、SQL注入等。
    解决这个问题的办法其实在Django的Debug错误页面已经给出了,我们需要在前端页面的form表单内添加一个{% csrf_token %}标签

      修改1. 有message信息则显示, 没有就不显示。
      修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
      修改3: Django提供了csrf防攻击的机制, 添加该信息则可顺利访问登陆界面
      修改4:name="username"指定表单内容存储的key值名称, eg: {"username":"你填的用户
      名","password":"你填的密码" }
    

//此文件是在原来基础上进行修改的
    <title>用户登录title>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-sm">
        div>
        <div class="col-sm">
            <h3 style="text-align: center">用户登录h3>
            # 修改1. 有message信息则显示, 没有就不显示。
            {% if message %}
            <div class="alert alert-warning" role="alert">
                <strong>登录失败!strong> {
    { message }}
            div>
            {% endif %}
		# 修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
            <form action="/login/" method="post">
            # 修改3: Django提供了csrf防攻击的机制, 添加该信息则可顺利访问登陆界面
                {% csrf_token %}
                <div class="form-group">
                    <label>用户名label>
                    # 修改4:name="username"指定表单内容存储的key值名称, eg:
{"username":"你填的用户名"}
                    <input type="text" class="form-control" name="username">
                div>
                <div class="form-group">
                    <label>Passwordlabel>
                    <input type="password" class="form-control" name="password">
                    <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.small>
                div>
                <a href="/register/" class="text-success">
                    <ins>新用户注册ins>
                a>
                <button type="submit" class="btn btn-primary float-right">登录button>
            form>

        div>
        <div class="col-sm">

        div>
    div>
div>

python(十五)--Django实例以及原理剖析_第43张图片

7.1.2 视图函数的完善(login)

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username').strip()
        password = request.POST.get('password').strip()
        print(username, password)
        print(request.POST)
        if username and password:
            user = SiteUser.objects.filter(name=username,password=password).first()
            if user:
                return redirect('/index/')
            else:
                message = "用户名或者密码错误"
            return render(request, 'login/login.html',{
     'message': message})
        else:
            message = "非法的数据信息"
            return render(request, 'login/login.html', {
     'message': message})
    return render(request, 'login/login.html')

7.1.3 测试是否成功

  • 访问网址:http:127.0.0.1:8000/login/ 正确的用户名和密码或者错误的用户名和密码

python(十五)--Django实例以及原理剖析_第44张图片
python(十五)--Django实例以及原理剖析_第45张图片

7.1.4 git提交

8.session会话和登录的视图函数

8.1 登陆成功,存储登录的用户信息到session中

python(十五)--Django实例以及原理剖析_第46张图片

8.1.1 登录时

# login/views.py
def login(request):
    if request.method == 'POST':
        username = request.POST.get('username').strip()
        password = request.POST.get('password').strip()
        #print(username, password)
        #print(request.POST)
        if username and password:
            user = SiteUser.objects.filter(name=username,password=password).first()
            if user:
                # ------------核心修改的内容开始
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['username'] = user.name
                # --------------核心修改的内容结束
                return redirect('/index/')
            else:
                message = "用户名或者密码错误"
            return render(request, 'login/login.html',{
     'message': message})
        else:
            message = "非法的数据信息"
            return render(request, 'login/login.html', {
     'message': message})
    return render(request, 'login/login.html')

8.1.2 登出时,清空session信息

# login/views.py
def logout(request):
    # 如果状态不是登录状态,则无法登出。
    if request.session.get('is_login'):
        request.session.flush()  # 清空session信息
    # redirect: 重定向(跳转)
    return redirect('/login/')

8.1.3 添加登出的超链接

# templates/login/index.html
# 核心代码如下:
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="#">主页a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon">span>
    button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item active">
                <a class="nav-link" href="#">Home <span class="sr-only">(current)span>a>
            li>
            <li class="nav-item">
                <a class="nav-link" href="#">Linka>
            li>
            <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
                   aria-haspopup="true" aria-expanded="false">
                    Dropdown
                a>
                <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                    <a class="dropdown-item" href="#">Actiona>
                    <a class="dropdown-item" href="#">Another actiona>
                    <div class="dropdown-divider">div>
                    <a class="dropdown-item" href="#">Something else herea>
                div>
            li>
            <li class="nav-item">
                <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disableda>
            li>
        ul>
        <form class="form-inline my-2 my-lg-0">
            <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Searchbutton>
        form>
        <ul class="navbar-nav">
            <li class="nav-item">
                <a class="nav-link" href="/logout/">登出a>
            li>

            <li class="nav-item">
                <a class="nav-link" href="/index/">{
    { request.session.username }}a>
            li>
        ul>
    div>
nav>

<h1>你好, {
    { request.session.username }}, 这是首页的模拟界面h1>

python(十五)--Django实例以及原理剖析_第47张图片

8.1.4 测试(index)

python(十五)--Django实例以及原理剖析_第48张图片

8.1.5 git提交

python(十五)--Django实例以及原理剖析_第49张图片

9.上传到giee

9.1 新建仓库

python(十五)--Django实例以及原理剖析_第50张图片

9.2 上传到giie仓库准备工作

9.2.1 创建README.md文件

# loginRegister
##项目介绍
##软件架构
##安装教程(基于互联网)
\```bash
git clone xxxx
# 如何生成requirements.txt文件?pip freeze  > requirements.txt
pip -r install requirements.txt  # pip安装文件中所有的第三方模块
cd xxxx
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
\```
浏览器访问网址: http://127.0.0.1:8000/index

##使用说明
- 可以运行多个平台

python(十五)--Django实例以及原理剖析_第51张图片

9.2.2 生成requirements.txt文件(所需的包)

pip freeze > requirements.txt

在这里插入图片描述
python(十五)--Django实例以及原理剖析_第52张图片

9.2.3 绑定gitee仓库

git remote add origin https://gitee.com/zhang-yu-1/login-register.git

python(十五)--Django实例以及原理剖析_第53张图片

在这里插入图片描述

9.2.4 git提交

python(十五)--Django实例以及原理剖析_第54张图片

9.3 上传仓库

git push --set-upstream origin master -f

python(十五)--Django实例以及原理剖析_第55张图片
python(十五)--Django实例以及原理剖析_第56张图片

10.查看之前的修改内容

10.1 从gitee仓库查看

python(十五)--Django实例以及原理剖析_第57张图片
python(十五)--Django实例以及原理剖析_第58张图片

10.2 命令行方式

git branch back-morning   ##创建新的分支
git branch    ##查看当前分支
git log     ##查看commit码
git reset --hard  c932da0a7dab3    ##版本回退
delete   ##删除分支

python(十五)--Django实例以及原理剖析_第59张图片
python(十五)--Django实例以及原理剖析_第60张图片
在这里插入图片描述
python(十五)--Django实例以及原理剖析_第61张图片
python(十五)--Django实例以及原理剖析_第62张图片
python(十五)--Django实例以及原理剖析_第63张图片

11. 图片验证码(captcha)

11.1 第三方库的设置(验证码插件)

  • 为了防止机器人频繁登录网站或者破坏分子恶意登录,很多用户登录和注册系统都提供了图形验证码功能。
  • 在Django中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发(不必重复造轮子)。这个库叫做django-simple-captcha。
    具体教程戳我
1. pip install django-simple-captcha    ##安装插件
2. settings设置app  #loginRegister/setting.py
3. python manage.py migrate
4. 查看是否添加到数据库
5. 添加信息到#loginRegister/urls.py
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('', include('login.urls')),
	    path('captcha/', include('captcha.urls')),
	]

python(十五)--Django实例以及原理剖析_第64张图片
python(十五)--Django实例以及原理剖析_第65张图片
python(十五)--Django实例以及原理剖析_第66张图片
python(十五)--Django实例以及原理剖析_第67张图片
python(十五)--Django实例以及原理剖析_第68张图片

11.2 git提交(验证码插件的设置)

python(十五)--Django实例以及原理剖析_第69张图片

11.3 Django表单

  • 我们前面都是手工在HTML文件中编写表单form元素,然后在views.py的视图函数中接收表单中的用户数据,再编写验证代码进行验证,最后使用ORM进行数据库的增删改查。这样费时费力,整个过程比较复杂,而且有可能写得不太恰当,数据验证也比较麻烦。设想一下,如果我们的表单拥有几十上百个数据字段,有不同的数据特点,如果也使用手工的方式,其效率和正确性都将无法得到保障。有鉴于此,Django在内部集成了一个表单功能,以面向对象的方式,直接使用Python代码生成HTML表单代码,专门帮助我们快速处理表单相关的内容。

  • Django的表单给我们提供了下面三个主要功能:
    1.准备和重构数据用于页面渲染;
    2.为数据创建HTML表单元素;
    3.接收和处理用户从表单发送过来的数据。

  • 编写Django的form表单,非常类似我们在模型系统里编写一个模型。在模型中,一个字段代表数据表的一列,而form表单中的一个字段代表 中的一个 元素。

11.3.1 创建表单模型

# /login/forms.py(新建的文件)
from captcha.fields import CaptchaField
from django import forms
class LoginForm(forms.Form):
    username = forms.CharField(label='用户名', required=True,min_length=4, max_length=128)
    password = forms.CharField(label="密码", required=True, min_length=4, max_length=10)
    captcha = CaptchaField(label="验证码")

11.3.2 视图逻辑优化

# login/views.py
def login(request):
    # 请求方法为POST提交
    if request.method == 'POST':
        # 修改1: 实例化表单对象
        login_form = LoginForm(request.POST)
        # 修改2: 验证表单数据的合法性
        if login_form.is_valid():
            # 修改3:获取表单填写的数据,数据清洗
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            user = SiteUser.objects.filter(name=username,
                                           password=password).first()
            if user:
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['username'] = user.name
                return redirect('/index/')
            else:
                message = "用户名或者密码错误"
                # 修改4: locals()以字典方式返回当前所有的变量
                # eg:{'message':'xxxx', 'login_form':'xxx'}
                return render(request, 'login/login.html', locals())
        else:
            message = "填写的登录信息不合法"
            return render(request, 'login/login.html', locals())
    # 请求方法是GET请求
    login_form = LoginForm()
    return render(request, 'login/login.html', locals())

11.3.3 Template页面优化

修改内容可以参考仓库git提交信息
两个都可以参考

# templates/login/login.html(部分修改)
<h3 style="text-align: center">用户登录h3>
# 修改1: 不同的报错,提示不同的信息
{% if login_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
	<strong>登录失败!strong> 验证码不正确
div>
{% elif message %}
<div class="alert alert-warning" role="alert">
	<strong>登录失败!strong> {
    { message }}
div>
{% endif %}
<div class="form-group">
	# 修改2:
	<label>{
    { login_form.username.label }}label>
	<input type="text" class="form-control" name="username">
div>
<div class="form-group">
	# 修改3:
	<label>{
    { login_form.password.label }}label>
	<input type="password" class="form-control" name="password">
	<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.small>
div>
# 修改4: 最重要的,添加验证码表单
<div class="form-group">
	<label>{
    { login_form.captcha.label }}label>
	{
    { login_form.captcha }}
div>

11.3.4 验证是否正确

python(十五)--Django实例以及原理剖析_第70张图片

11.3.5 git 提交

python(十五)--Django实例以及原理剖析_第71张图片

13. 邮箱注册

13.1 获取授权码并开启SMTP

python(十五)--Django实例以及原理剖析_第72张图片
python(十五)--Django实例以及原理剖析_第73张图片
python(十五)--Django实例以及原理剖析_第74张图片
python(十五)--Django实例以及原理剖析_第75张图片

13.2 配置邮件信息

# loginRegister/settings.py
# mail configure(添加信息如下)
EMAIL_HOST = 'smtp.qq.com' # 'smtp.163.com'
EMAIL_PORT = 465   ##qq接收和发送有不同的端口(此处是发送)
EMAIL_HOST_USER = 'xxxx' # 你的邮箱地址
#授权码一个是发送邮件IMAP/SMTP,接收邮件使用的是pop3
EMAIL_HOST_PASSWORD = 'xxxxxxx' # 不是邮箱的登录密码,而是授权码(如何获取授权码)
EMAIL_USE_SSL = True # 不开启ssl(qq设置为True,163设置为False)

13.3 交互式环境中测试发送邮件是否成功?

Terminal输入命令> python manage.py shell
In [1]: from django.core.mail import send_mail
In [2]: from loginRegister.settings import EMAIL_HOST_USER
In [3]: send_mail("测试邮件", "content", EMAIL_HOST_USER,['[email protected]'])

python(十五)--Django实例以及原理剖析_第76张图片
python(十五)--Django实例以及原理剖析_第77张图片

13.4 注册表单

# login/forms.py
class RegisterForm(forms.Form):
	username = forms.CharField(label="用户名", required=True, max_length=128)
	password1 = forms.CharField(label="密码", max_length=256, required=True)
	password2 = forms.CharField(label="确认密码", max_length=256, required=True)
	email = forms.EmailField(label="邮箱地址")
	captcha = CaptchaField(label='验证码')

python(十五)--Django实例以及原理剖析_第78张图片

13.4 实现注册视图

  • 如果用户已经登录,则不能注册跳转到首页。
  • 如果是GET请求,返回用户注册的html页面。
  • 如果是POST请求, 先验证提交的数据是否通过,清洗数据。 接下来判断用户名和邮箱是否已经被注册, 将注册的信息存储到数据库,跳转到登录界面。
  • 额外功能: 为了数据的安全性注册时,密码存储到数据库不是明文存储,而是先加密再存储。
# login/views.py
from login.forms import LoginForm, RegisterForm
def register(request):
	# 如果用户已经登录,则不能注册跳转到首页。
	if request.session.get('is_login', None):
		return redirect('/index/')
	# 如果是POST请求
	if request.method == 'POST':
		print(request.POST)
		register_form = RegisterForm(request.POST)
		message = "请检查填写的内容!"
		# 先验证提交的数据是否通过
		if register_form.is_valid():
			# 清洗数据
			username = register_form.cleaned_data.get('username')
			password1 = register_form.cleaned_data.get('password1')
			password2 = register_form.cleaned_data.get('password2')
			email = register_form.cleaned_data.get('email')
			#print(locals())
			# 接下来判断用户名和邮箱是否已经被注册
			same_name_user = SiteUser.objects.filter(name=username)
			#print(same_name_user)
			if same_name_user:
				message = '用户名已经存在'
				return render(request, 'login/register.html', locals())
			same_email_user = SiteUser.objects.filter(email=email)
			if same_email_user:
				message = '该邮箱已经被注册了!'
				return render(request, 'login/register.html', locals())
			# 将注册的信息存储到数据库,跳转到登录界面
			new_user = SiteUser(name=username, password=password1, email=email)
			new_user.save()
			return redirect('/login/')
	# 如果是GET请求,返回用户注册的html页面。
	register_form = RegisterForm()
	return render(request, 'login/register.html', locals())

13.5 template的修改

# templates/login/register.html
<h3 style="text-align: center">用户注册h3>
{% if register_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
	<strong>注册失败!strong> 验证码不正确
div>
{% elif message %}
<div class="alert alert-warning" role="alert">
	<strong>注册失败!strong> {
    { message }}
div>
{% endif %}

<form action="/register/" method="post">
	{% csrf_token %}
	<div class="form-group">
		<label>{
    { register_form.username.label }}label>
		<input type="text" class="form-control" name="username">
	div>
	<div class="form-group">
		<label>{
    { register_form.email.label }}label>
		<input type="email" class="form-control" name="email">
	div>
	<div class="form-group">
		<label>{
    { register_form.password1.label }}label>
		<input type="password" class="form-control" name="password1">
		<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.small>
	div>
	<div class="form-group">
		<label>{
    { register_form.password2.label }}label>
		<input type="password" class="form-control" name="password2">
		<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.small>
	div>
	<div class="form-group">
		<label>{
    { register_form.captcha.label }}label>{
    { register_form.captcha }}
	div>
	<a href="/login/" class="text-success">
		<ins>用户登录ins>
	a>
	<button type="submit" class="btn btn-primary float-right">注册button>
form>

13.6 测试是否成功

python(十五)--Django实例以及原理剖析_第79张图片
python(十五)--Django实例以及原理剖析_第80张图片

13.7 注册添加密码加密功能

  • 对于如何加密密码,有很多不同的途径,其安全程度也高低不等。这里我们使用Python内置的hashlib库,使用哈希值的方式加密密码,可能安全等级不够高,但足够简单,方便使用。
#在 login/utils.py 中编写一个hash函数:
def hash_code(s, salt='mysite'):# 加点盐
	h = hashlib.sha256()
	s += salt
	h.update(s.encode()) # update方法只接收bytes类型
	return h.hexdigest()
#在 login/views.py 中修改login和register视图
def register(request):
	# .....省略部分代码
	new_user = SiteUser(name=username, password=hash_code(password1),
	email=email)
	# .....省略部分代码
def login(request):
	# .....省略部分代码
	user = SiteUser.objects.filter(name=username,password=hash_code(password)).first()
	# .....省略部分代码

13.8 邮箱注册确认(是否是真正的邮箱)

  • 很自然地,我们会想到如果能用邮件确认的方式对新注册用户进行审查,既安全又正式,也是目前很多站点的做法。

13.8.1 创建模型

  • 既然要区分通过和未通过邮件确认的用户,那么必须给用户添加一个是否进行过邮件确认的属性。另外,我们要创建一张新表,用于保存用户的确认码以及注册提交的时间。
# /login/models.py
class SiteUser(models.Model):
	# .......
	has_confirmed = models.BooleanField(default=False, verbose_name="是否邮箱验证")

class ConfirmString(models.Model):
    code = models.CharField(max_length=256, verbose_name="确认码")
    user = models.OneToOneField('SiteUser', on_delete=models.CASCADE)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    def __str__(self):
        return self.user.name + ":" + self.code
    class Meta:
        ordering = ["-create_time"]
        verbose_name = "确认码"
        verbose_name_plural = "确认码"

数据库模型更改,一定要生成迁移脚本和写入数据库。

python manage.py makemigrations 
python manage.py migrate

python(十五)--Django实例以及原理剖析_第81张图片

顺便修改一下admin.py文件,方便我们在后台修改和观察数据

# login/admin.py
admin.site.register(ConfirmString)

python(十五)--Django实例以及原理剖析_第82张图片

13.8.2 修改视图

#login/utils.py
#make_confirm_string() 是创建确认码对象的方法,代码如下:

from django.core.mail import send_mail
from login.models import ConfirmString
from loginRegister import settings

import datetime
def make_confirm_string(user):
    """生成确认码"""
    print("生成确认码.....")
    # 获取当前时间
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    code = hash_code(user.name, now)
    print('in code:',code)
    ConfirmString.objects.create(code=code, user=user)
    return code

#send_email(email, code) 方法接收两个参数,分别是注册的邮箱和前面生成的哈希值,代码如下:
def send_email(email, code):
    print('send mail.........')
    subject = '注册确认邮件'
    text_content = '''感谢注册,这里是登录注册系统网站!\
                    如果你看到这条消息,说明你的邮箱服务器不提供HTML链接功能,请联系管理员!'''
    html_content = '''
    

感谢注册点击验证,\ 这里是登录注册系统网站!

请点击站点链接完成注册确认!

此链接有效期为{}天!

'''
.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS) send_mail(subject, text_content,settings.EMAIL_HOST_USER, [email, ], html_message=html_content)
##修改的注册视图函数  login/views.py
def register(request):
    # 如果用户已经登录,则不能注册跳转到首页。
    if request.session.get('is_login', None):
        return redirect('/index/')
    # 如果是POST请求
    if request.method == 'POST':
        print(request.POST)
        register_form = RegisterForm(request.POST)
        message = "请检查填写的内容!"
        # 先验证提交的数据是否通过
        if register_form.is_valid():
            # 清洗数据
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')

            # 接下来判断用户名和邮箱是否已经被注册
            same_name_user = SiteUser.objects.filter(name=username)
            print(same_name_user)
            if same_name_user:
                message = '用户名已经存在'
                return render(request, 'login/register.html', locals())
            same_email_user = SiteUser.objects.filter(email=email)
            if same_email_user:
                message = '该邮箱已经被注册了!'
                return render(request, 'login/register.html', locals())
            try:
                # 将注册的信息存储到数据库,跳转到登录界面
                new_user = SiteUser(name=username, password=hash_code(password1), email=email)
                new_user.save()
                # 生成确认码并发送确认邮件
                code = make_confirm_string(new_user)
                print('code:', code)
                send_email(email, code)
                message = '请前往邮箱进行确认!'
            except Exception:
                new_user.delete()
                message = '发送邮件失败!'
                #print(Exception)
                return render(request, 'login/register.html', locals())
            else:
                return  redirect('/login/')
    # 如果是GET请求,返回用户注册的html页面。
    register_form = RegisterForm()
    return render(request, 'login/register.html', locals())

13.8.3 邮件相关的设置

  • 最后的有效期天数为设置在settings中的 CONFIRM_DAYS 。下面是邮件相关的settings配置:
# 注册有效期天数
CONFIRM_DAYS = 3

python(十五)--Django实例以及原理剖析_第83张图片

13.8.4 测试

  • 注册一个用户,判断是否能收到确认邮件。

python(十五)--Django实例以及原理剖析_第84张图片
python(十五)--Django实例以及原理剖析_第85张图片
python(十五)--Django实例以及原理剖析_第86张图片

13.8.5 处理邮件确认请求

1.在login子应用的 urls.py 中添加一条url

path('confirm/', views.user_confirm,name='confirm'),

python(十五)--Django实例以及原理剖析_第87张图片

其次,在 login/views.py 中添加一个 user_confirm 视图。

- 	获取确认码信息
	数据库中是否有该确认码,如果没有, 返回说是无效的请求
	数据库中是否有该确认码,如果有, 判断是否过期? 如果过期,删除用户信息,否则更新用户信息。
# login/views.py
def user_confirm(request):
    code = request.GET.get('code', None)
    message = ''
    try:
        confirm = ConfirmString.objects.get(code=code)
    except:
        message = '无效的确认请求!'
        return render(request, 'login/confirm.html', locals())

    create_time = confirm.create_time
    now = datetime.now()
    print(now, create_time, create_time + timedelta(settings.CONFIRM_DAYS))
    if now > create_time + timedelta(settings.CONFIRM_DAYS):
        confirm.user.delete()
        message = '您的邮件已经过期!请重新注册!'
    else:
        confirm.user.has_confirmed = True    ##更新的字段要和数据库存储的字段保持一致,不是is_confirmed,而是has_confirmed
        confirm.user.save()
        confirm.delete()
        message = '感谢确认,请使用账户登录!'
    return render(request, 'login/confirm.html', locals())

设置confirm.html 页面

  • 需要一个 confirm.html 页面,我们将它创建在 /login/templates/login/ 下面:页面中通过JS代码,设置2秒后自动跳转到登录页面,可根据自己的需要去除或者美化。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<h1 style="margin-left: 100px;">{
    { message }}h1>

<script>
window.setTimeout("window.location='/login/'",2000);
script>

body>
html>

python(十五)--Django实例以及原理剖析_第88张图片

13.8.6 修改登录规则

  • 既然未进行邮件确认的用户不能登录,那么我们就必须修改登录规则,如下所示:
def login(request):
    if request.method == 'POST':
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            print('------', password)
            users = SiteUser.objects.filter(name=username).first()
            print('-----users:', users.password)
            print(hash_code(password))

            user = SiteUser.objects.filter(name=username, password=hash_code(password)).first()
            print('------------', user)
            if user:
                if not user.has_confirmed:   ##登录规则
                    message = '该用户还未经过邮件确认!'
                    return render(request, 'login/login.html', locals())
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['username'] = user.name
                return redirect('/index/')
            else:
                message = "用户名或者密码错误"
                return render(request, 'login/login.html', locals())
        else:
            message = "填写的登录信息不合法"
            return render(request, 'login/login.html', locals())
    login_form = LoginForm()
    return render(request, 'login/login.html', locals())

13.8.7 测试

python(十五)--Django实例以及原理剖析_第89张图片

python(十五)--Django实例以及原理剖析_第90张图片
python(十五)--Django实例以及原理剖析_第91张图片
python(十五)--Django实例以及原理剖析_第92张图片

python(十五)--Django实例以及原理剖析_第93张图片

14. 其他

如何在lwindows安装redis

你可能感兴趣的:(Django,python,django)