文章目录
一、可重用注册登录系统
- 注册(邮箱注册,手机,微信,QQ)
- 登录
- 注销
1.项目开始前的思考
- 路由配置
urlpattern = [
path('/register/', views.register),
path('/login/', views.login),
path('/logout/', views.logout),
]
- 视图配置(重点)----
- 数据库模型Model:
class User:
id, name, password, email, create_time, update_time
last_time(最后一次登录的时间), gender, province
- 模板Template: register.html, login.html, index.html
2、搭建项目环境
-
新建project->Django项目,选择虚拟环境:是个隔离的环境,与python无关
-
创建app
#Terminal命令行执行
python manage.py startapp login
- 设置时区和语言
# 编辑文件 loginRegister/settings.py
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'
- 数据库表生成
#Terminal命令行执行
python manage.py makemigrations
python manage.py migrate # 将迁移脚本的内容写入数据库并创建数据库表
python manage.py createsuperuser # 创建后台登录的超级用户和密码
- 启动开发服务器
方法1:命令启动
#Terminal命令行执行
python manage.py runserver
方法2:配置Django Server
- 浏览器访问,检测是否成功?
访问网址: http://127.0.0.1:8000/
访问网址: http://127.0.0.1:8000/admin/
- git提交项目代码到本地仓库
学习Git基本操作, 参考链接: https://www.liaoxuefeng.com/wiki/896043488029600
安装Git,并添加环境变量
#Terminal命令行执行
git init # 安装插件.ignore, 并生成python上传git项目需要忽略内容的文件.gitignore
git add * # 添加修改到暂存区
git commit -m "搭建项目开发环境" # 将暂存区的代码提交到本地git仓库 $
git log # 查看历史提交记录
安装.ignore插件
3.设计数据库模型
-
作为一个用户登录和注册项目,需要保存的都是各种用户的相关信息。很显然,我们至少需要一张用户表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(默认)的话,字段没被赋值则会抛错;和数据验证(表单验证等)有
关
1)数据库模型文件
# 编辑login/models.py
from django.db import models
# Create your models here.
class SiteUser(models.Model):
"""用户的数据库模型,注册/登录需要"""
gender_choice=(
(0,"未知"),
(1,"男"),
(2,"女"),
)
name=models.CharField(max_length=128,unique=True,verbose_name="用户名")#unique=True用户唯一
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
2)设置数据库后端
Django支持MySQL, Sqlite, oracle等数据库, 此处选择默认的sqlite,不做修改。
3)注册app
4)生成迁移脚本并写入数据库
#Terminal命令行执行
python manage.py makemigrations
python manage.py migrate # 将迁移脚本的内容写入数据库并创建数据库表
5)测试是否成功
打开数据库文件db.sqlite3, 查看是否有数据库表login_siteuser,如果有,则操作成功。
6)数据库后台管理
from django.contrib import admin
from login.models import SitrUser
# Register your models here.
# 后台管理设置信息
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(SitrUser,SiteUserAdmin)
浏览器访问,检测是否成功访问网址: http://127.0.0.1:8000/admin/
4.路由与视图函数框架搭建
路由设计:
URL 视图views 模板 功能
/index/ login.views.index index.html 首页
/login/ login.views.login login.html 登录页面
/register/ login.views.register register.html 注册界面
/logout/ login.views.logout 无需返回页面 登出界面
访问策略:
- 未登录人员,不论是访问index还是login和logout,全部跳转到login界面
- 已登录人员,访问login会自动跳转到index页面
- 已登录人员,不允许直接访问register页面,需先logout
- 登出后,自动跳转到login界面
1)路由配置
- 主URL访问子URL,子URL访问主页和详情页
#主URL,loginRegister/urls.py:
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('login.urls'))
]
#子URL,login/urls.py(新建的文件)
from django.urls import path, include
from login import views
urlpatterns = [
path('index/', views.index, name='index'),
path('login/', views.login, name='login'),
path('register/', views.register, name='register'),
path('logout/', views.logout, name='logout'),
]
2)视图函数的配置
# 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/')
3)模板template的配置
- templates/login/index.html(新建)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>这是首页的模拟界面</h1>
</body>
</html>
- templates/login/login.html(新建)
<!DOCTYPE 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(新建)
<!DOCTYPE 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>
测试是否成功
浏览器访问,检测是否成功?(第一步完美搞定)
访问网址: http://127.0.0.1:9999/index/
访问网址: http://127.0.0.1:9999/login/
访问网址: http://127.0.0.1:9999/register/
templates/login/index.html
<!doctype html>
<html lang="zh-CN">
<head>
<!-- 必须的 meta 标签 -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap 的 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
<title>首页</title>
</head>
<body>
<h1>这是首页的模拟界面</h1>
<!-- JavaScript 文件是可选的。从以下两种建议中选择一个即可! -->
<!-- 选项 1:jQuery 和 Bootstrap 集成包(集成了 Popper) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV" crossorigin="anonymous"></script>
<!-- 选项 2:Popper 和 Bootstrap 的 JS 插件各自独立 -->
<!--
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha384-gRC4eoaRyQ8xv2X6Mnf+eOIrtON3wId3dAkwO0HQX26OrFBoLpjX/XWOJacSiZhL" crossorigin="anonymous"></script>
-->
</body>
</html>
templates/login/login.html
<html lang="zh-CN">
<head>
<!-- 必须的 meta 标签 -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap 的 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/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>Password</label>
<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>
<!-- JavaScript 文件是可选的。从以下两种建议中选择一个即可! -->
<!-- 选项 1:jQuery 和 Bootstrap 集成包(集成了 Popper) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV"
crossorigin="anonymous"></script>
<!-- 选项 2:Popper 和 Bootstrap 的 JS 插件各自独立 -->
<!--
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha384-gRC4eoaRyQ8xv2X6Mnf+eOIrtON3wId3dAkwO0HQX26OrFBoLpjX/XWOJacSiZhL" crossorigin="anonymous"></script>
-->
</body>
</html>
templates/login/register.html
<!doctype html>
<html lang="zh-CN">
<head>
<!-- 必须的 meta 标签 -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap 的 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/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>
<!-- JavaScript 文件是可选的。从以下两种建议中选择一个即可! -->
<!-- 选项 1:jQuery 和 Bootstrap 集成包(集成了 Popper) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV"
crossorigin="anonymous"></script>
<!-- 选项 2:Popper 和 Bootstrap 的 JS 插件各自独立 -->
<!--
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha384-gRC4eoaRyQ8xv2X6Mnf+eOIrtON3wId3dAkwO0HQX26OrFBoLpjX/XWOJacSiZhL" crossorigin="anonymous"></script>
-->
</body>
</html>
5.前端界面设计与优化
1)完善登录的视图函数
- html和视图函数交互的完善
修改1. 有message信息则显示, 没有就不显示。
修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
修改3: Django提供了csrf防攻击的机制, 添加该信息则可顺利访问登陆界面
修改4:name="username"指定表单内容存储的key值名称, eg: {“username”:“你填的用户
名”,“password”:“你填的密码” }
#编辑templates/login/login.html
<!doctype html>
<html lang="zh-CN">
<head>
<!-- 必须的 meta 标签 -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap 的 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/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>
# 修改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>Password</label>
<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>
<!-- JavaScript 文件是可选的。从以下两种建议中选择一个即可! -->
<!-- 选项 1:jQuery 和 Bootstrap 集成包(集成了 Popper) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV"
crossorigin="anonymous"></script>
<!-- 选项 2:Popper 和 Bootstrap 的 JS 插件各自独立 -->
<!--
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha384-gRC4eoaRyQ8xv2X6Mnf+eOIrtON3wId3dAkwO0HQX26OrFBoLpjX/XWOJacSiZhL" crossorigin="anonymous"></script>
-->
</body>
</html>
2)视图函数的完善
#编辑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):
if request.method == 'POST':
username = request.POST.get('username').strip()
password = request.POST.get('password').strip()
# print(username, password)
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')
def register(request):
pass
return render(request, 'login/register.html')
def logout(request):
pass
# redirect: 重定向(跳转)
return redirect('/login/')
浏览器访问,检测是否成功?
访问网址: http://127.0.0.1:9999/login/
填写正确的用户名和密码/错误的用户名和密码测试是否为期待的效果。
6.session会话与登录的视图函数
- 登录成功, 存储登录的用户信息到session中
# 编辑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)
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
# --------------核心修改的内容结束
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')
- 登出时,清空session信息
# 编辑login/views.py
def logout(request):
# 如果状态不是登录状态,则无法登出。
if request.session.get('is_login'):
request.session.flush() # 清空session信息
return redirect('/login/')
- 在首页添加登出的超链接并测试
<body>
<h1>你好, {{ request.session.username }}, 这是首页的模拟界面</h1>
<a href="/logout/"><strong style="font-size: 20px">登出</strong></a>
访问网址: http://127.0.0.1:9999/index/
项目地址: https://gitee.com/half-summer/loginregister
7.验证码
为了防止机器人频繁登录网站或者破坏分子恶意登录,很多用户登录和注册系统都提供了图形验证码功能。在Django中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发(不必重复造轮子)。这个库叫做django-simple-captcha。
官方文档:https://django-simple-captcha.readthedocs.io/en/latest/usage.html
1)验证码安装
#Terminal命令行执行
pip install django-simple-captcha
#Terminal命令行执行
python manage.py migrate
2)Django表单
我们前面都是手工在HTML文件中编写表单form元素,然后在views.py的视图函数中接收表单中的用户数据,再编写验证代码进行验证,最后使用ORM进行数据库的增删改查。这样费时费力,整个过程比较复杂,而且有可能写得不太恰当,数据验证也比较麻烦。设想一下,如果我们的表单拥有几十上百个数据字段,有不同的数据特点,如果也使用手工的方式,其效率和正确性都将无法得到保障。有鉴于此,Django在内部集成了一个表单功能,以面向对象的方式,直接使用Python代码生成HTML表单代码,专门帮助我们快速处理表单相关的内容。
Django的表单给我们提供了下面三个主要功能:
– 准备和重构数据用于页面渲染;
– 为数据创建HTML表单元素;
– 接收和处理用户从表单发送过来的数据。
– 编写Django的form表单,非常类似我们在模型系统里编写一个模型。在模型中,一个字段代表数据表的一列,而form表单中的一个字段代表 中的一个 元素。
官方文档:https://docs.djangoproject.com/zh-hans/3.1/topics/forms/
- 创建表单模型
# /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="验证码")
- 视图逻辑优化
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
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())
- Template页面优化
# 编辑templates/login/login.html
<!doctype html>
<html lang="zh-CN">
<head>
<!-- 必须的 meta 标签 -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap 的 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/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>
# 修改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 %}
<form action="/login/" method="post">
{% csrf_token %}
<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>
<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>
<!-- JavaScript 文件是可选的。从以下两种建议中选择一个即可! -->
<!-- 选项 1:jQuery 和 Bootstrap 集成包(集成了 Popper) -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-LCPyFKQyML7mqtS+4XytolfqyqSlcbB3bvDuH9vX2sdQMxRonb/M3b9EmhCNNNrV"
crossorigin="anonymous"></script>
<!-- 选项 2:Popper 和 Bootstrap 的 JS 插件各自独立 -->
<!--
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha384-gRC4eoaRyQ8xv2X6Mnf+eOIrtON3wId3dAkwO0HQX26OrFBoLpjX/XWOJacSiZhL" crossorigin="anonymous"></script>
-->
</body>
</html>
- 验证是否正确
8.邮箱注册
1)发送邮件功能测试
- 配置邮件信息
# 编辑loginRegister/settings.py
# mail configure(添加信息如下)
EMAIL_HOST = 'smtp.163.com' # 'smtp.qq.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'gfy17737466417@163.com' # 你的邮箱地址
EMAIL_HOST_PASSWORD = 'PJIKJQNZJJWDOLTA' # 不是邮箱的登录密码,而是授权码(如何获 取授权码)
EMAIL_USE_SSL = False # 不开启ssl
- 如何获取授权码?:注册登录163邮箱,生成成功后复制授权码
#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,['gfy17737466417@163.com'])
- 验证是否成功? 查看是否受到邮件?
2)基本的注册功能实现
- 注册表单
#编辑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='验证码')
- 实现注册视图
– 如果用户已经登录,则不能注册跳转到首页。
– 如果是GET请求,返回用户注册的html页面。
– 如果是POST请求, 先验证提交的数据是否通过,清洗数据。 接下来判断用户名和邮箱是-- 否已经被
– 注册, 将注册的信息存储到数据库,跳转到登录界面。
– 额外功能: 为了数据的安全性注册时,密码存储到数据库不是明文存储,而是先加密再存储。
# 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')
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())
- Template模板的更改
# templates/login/register.html
<div class="col-sm">
<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>
- 测试注册用户是否成功
注册添加密码加密功能
对于如何加密密码,有很多不同的途径,其安全程度也高低不等。这里我们使用Python内置的hashlib库,使用哈希值的方式加密密码,可能安全等级不够高,但足够简单,方便使用。
#新建 login/utils.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/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()
3)邮件注册确认
很自然地,我们会想到如果能用邮件确认的方式对新注册用户进行审查,既安全又正式,也是目前很多站点的做法。
- 创建模型
既然要区分通过和未通过邮件确认的用户,那么必须给用户添加一个是否进行过邮件确认的属性。另外,我们要创建一张新表,用于保存用户的确认码以及注册提交的时间
# /login/models.py
class SiteUser(models.Model):
# class SiteUser最后添加
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
修改一下admin.py文件,方便我们在后台修改和观察数据
#login/admin.py
from login.models import SiteUser,ConfirmString
admin.site.register(ConfirmString)#添加
- 修改视图
#login/view.py
def register(request):
# ................ code = make_confirm_string(new_user)
send_email(email, code)
message = '请前往邮箱进行确认!'
# ..................
make_confirm_string() 是创建确认码对象的方法;
send_email(email, code) 方法接收两个参数,分别是注册的邮箱和前面生成的哈希值,代码如下:
#在login/utils.py 文件添加:
import hashlib
import datetime
from django.core.mail import send_mail
from login.models import ConfirmString
from loginRegister import settings
def hash_code(s, salt='mysite'):# 加点盐
h = hashlib.sha256()
s += salt
h.update(s.encode()) # update方法只接收bytes类型
return h.hexdigest()
def make_confirm_string(user):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
code = hash_code(user.name, now)
ConfirmString.objects.create(code=code, user=user,)
return code
def send_email(email, code):
print('send mail.........')
subject = '注册确认邮件'
text_content = '''感谢注册,这里是登录注册系统网站!\
如果你看到这条消息,说明你的邮箱服务器不提供HTML链接功能,请联系管理员!'''
html_content = '''
<p>感谢注册<a href="http://{}/confirm/?code={}" target=blank>点击验证</a>,\
这里是登录注册系统网站!</p>
<p>请点击站点链接完成注册确认!</p>
<p>此链接有效期为{}天!</p>
'''.format('127.0.0.1:9999', code, settings.CONFIRM_DAYS)
send_mail(subject, text_content,
settings.EMAIL_HOST_USER, [email, ], html_message=html_content)
最后的有效期天数为设置在settings中的 CONFIRM_DAYS 。下面是邮件相关的settings配置:
#loginRegister/settings.py
# 注册有效期天数
CONFIRM_DAYS = 3
- 测试:注册一个用户,判断是否能收到确认邮件。
4)处理邮件确认请求
在login子应用的 urls.py 中添加一条url:
#login/urls.py
path('confirm/', views.user_confirm,name='confirm'),
其次,在 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.is_confirmed = True
confirm.user.save()
confirm.delete()
message = '感谢确认,请使用账户登录!'
return render(request, 'login/confirm.html', locals())
需要一个 confirm.html 页面,我们将它创建在 /login/templates/login/ 下面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 style="margin-left: 100px;">{{ message }}</h1>
<script>
window.setTimeout("window.location='/login/'",2000);
</script>
</body>
</html>
页面中通过JS代码,设置2秒后自动跳转到登录页面,可根据自己的需要去除或者美化
5)修改登录规则
既然未进行邮件确认的用户不能登录,那么我们就必须修改登录规则,如下所示:
if not user.has_confirmed:
message = '该用户还未经过邮件确认!'
return render(request, 'login/login.html', locals())
去邮箱验证,然后登陆