Django 项目实践 —— 可重用注册登录系统 (1)
目录
可重用注册登录系统
- 注册(邮箱注册,手机,微信,QQ)
- 登录
- 注销
搭建项目环境
创建应用
- 创建Django项目
- 设置时区和语言
- 创建app
python manage.py startapp login
- 数据库表生成
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
- 启动开发服务器
# 启动服务,指定8080 端口
python manage.py runserver 8080
- 浏览器访问,检测是否成功?(第一步完美搞定)
访问网址: http://127.0.0.1:8080/
访问网址: http://127.0.0.1:8080/admin/
安装ignore 插件
过滤某些不想提交到git 本地库的文件
git提交项目代码到本地仓库
$ git init
# 安装插件.ignore, 并生成python上传git项目需要忽略内容的文件.gitignore
$ git add * # 添加修改到暂存区
$ git commit -m "搭建项目开发环境" # 将暂存区的代码提交到本地git仓库
$ git log # 查看历史提交记录
设计数据库模型
用户表分析
作为一个用户登录和注册项目,需要保存的都是各种用户的相关信息。很显然,我们至少需要一张用户 表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(默认)的话,字段没被赋值则会抛错;和数据验证(表单验证等)有 关
文件的配置
数据库模型文件(login/models.py)
from django.db import models
# 定义数据库模型
# Create your models here.
# appname_siteuser
class SiteUser(models.Model):
"""用户的数据库模型,注册/登陆需要"""
# gender_choice 性别
gender_choice = (
(0, "未知"),
(1, "男"),
(2, "女"),
)
# 姓名,字符串类型, unique=True (唯一标识)
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="电子邮件")
# 性别,默认0,未知
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
保存到本地 git 库
设置数据库后端
Django支持MySQL, Sqlite, oracle等数据库, 此处选择默认的sqlite,不做修改。
注册app(loginRegister/setting.py)
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 # 将迁移脚本的内容写入数据库并创建数据库表
测试是否成功 打开数据库文件db.sqlite3, 查看是否有数据库表login_siteuser,如果有,则操作成功。
数据库模型后台管理
数据库模型后台管理(login/admin.py)
from django.contrib import admin
# Register your models here.
from login.models import SiteUser
class SiteUseradmin(admin.ModelAdmin):
list_display = ['id', 'name', 'gender']
list_filter = ['gender', 'create_time']
search_fields = ['name']
admin.site.register(SiteUser, SiteUseradmin)
保存到本地 git库
查看本地 git 库提交日志
浏览器访问,检测是否成功?(完美搞定)
访问网址: http://127.0.0.1:8080/admin/
路由与视图函数框架搭建
路由设计
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界面
路由配置
主路由配置文件(loginRegister/urls.py)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('login.urls')),
]
子路由配置文件(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'),
path('login/', views.login, name='login'),
path('register/', views.register, name='register'),
path('logout/', views.logout, name='logout'),
]
视图函数的配置(login/views.py)
# 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/')
模板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>
保存到本地git 库
测试是否成功
浏览器访问,检测是否成功?(第一步完美搞定)
- 访问网址: http://127.0.0.1:8080/index/
- 访问网址: http://127.0.0.1:8080/login/
- 访问网址: http://127.0.0.1:8080/register/
前端界面设计与优化
在颜值即正义的年代,但没有CSS和JS,样子真的令人无法接受。 然而,大多数使用Django的人都不具备多高的前端水平,通常也没有专业的前端工程师配合,自己写的 CSS和JS却又往往惨不忍睹。怎么办?没关系,我们有现成的开源前端CSS框架!Bootstrap4就是最好 的CSS框架之一!
Bootstrap核心汇总:
首页美化
# template/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>
<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="#">Link</a>
</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="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</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">Search</button>
</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>
<!-- 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>
完善登录的视图函数
html和视图函数交互的完善
- 修改1. 有message信息则显示, 没有就不显示。
- 修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
- 修改3: Django提供了csrf防攻击的机制, 添加该信息则可顺利访问登陆界面
- 修改4:name="username"指定表单内容存储的key值名称, eg: {"username":"你填的用户 名","password":"你填的密码" }
templates/login/login.html
<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>
视图函数的完善(login/views.py)
# 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:
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')
保存到本地git 库
浏览器访问,检测是否成功?
- 访问网址: http://127.0.0.1:8080/login/
- 填写正确的用户名和密码/错误的用户名和密码测试是否为期待的效果。
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
# --------------核心修改的内容结束
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')
登出时,清空session信息(login/views.py)
def logout(request):
# 如果状态不是登录状态,则无法登出。
if request.session.get('is_login'):
request.session.flush() # 清空session信息
return redirect('/login/')
在首页添加登出的超链接并测试(templates/login/index.html)
# 核心代码如下:
<h1>你好, {{ request.session.username }}, 这是首页的模拟界面</h1>
<a href="/logout/"><strong style="font-size: 20px">登出</strong></a>
浏览器访问,检测是否成功?
访问网址: http://127.0.0.1:8080/index/
项目地址: https://gitee.com/half-summer/loginregister