Django提供了一套身份验证和授权的权限系统,允许验证用户凭证,并定义每个用户允许执行的操作。

权限系统框架包括了用户和分组的内置模型,用于登录用户的权限,指定用户是否可以执行任务、表单、视图,以及查看限制内容的工具。

Django身份验证系统为通用设计因此不提供其它Web身份验证系统中所提供的功能,对于某些常见问题可作为第三方软件包提供,比如限制登录尝试和针对第三方的身份验证

环境搭建

  • 操作系统 Windows10
  • 开发工具 Pycharm
$ python3 --version
Python 3.8.4
$ pip3 --version
pip 20.2.3 from d:\python\program\python38\lib\site-packages\pip (python 3.8)
$ django-admin --version
3.1.1

PIP安装Django

$ pip3 install django

创建项目

  • 使用django-admin项目管理工具创建Django项目
$ django-admin startproject gamesite

项目配置

$ vim gamesite/setting.py

配置项目时区中国

USE_TZ = False
TIME_ZONE = "Asia/Shanghai"

配置语言为中文

LANGUAGE_CODE = "zh-hans"

配置MySQL数据库

  • 使用PIP安装pymysql模块,用于连接MySQL数据库。
$ pip3 install pymysql
  • 项目配置文件中配置默认(default)数据库连接选项以连接MySQL数据库
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','HOST': '127.0.0.1','PORT': '3306','NAME': 'game','USER': 'root','PASSWORD': 'root','OPTIONS': {'init_command': 'SET sql_mode="STRICT_TRANS_TABLES"','charset': 'utf8mb4'}}
}
  • 安装项目默认迁移文件生成项目必需的数据表
$ python3 manage.py migrate

创建项目

创建游戏管理项目

进入项目根目录

$ cd gamesite

游戏管理项目日后会包含管理后台、官网、代理后台、对外接口等多个子项目,如何使用Django创建出多个子系统的项目工程呢?每个子系统下根据业务划分功能模块,每个功能模块对应一个应用。

如何创建子系统来管理多个应用功能模块呢?采用Python的package包的形式来作为子系统,进而管理子系统下的多个应用。

创建管理后台

  1. 在项目下创建Python Package命名为backend管理后台
  2. 选择backend文件夹右键选择"Make Directory as Sources"
  3. 在项目配置文件中添加backend文件夹
$ vim gamesite/settings.py
import os
import sys
from pathlib import PathBASE_DIR = Path(__file__).resolve().parent.parentsys.path.insert(0, os.path.join(BASE_DIR, "backend"))
  1. backend包下创建login应用和home应用并注册
$ vim gamesite/settings.py
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','backend.login.apps.LoginConfig','backend.home.apps.HomeConfig'
]

创建包

创建登录应用

创建游戏后台应用

$ python3 manage.py startapp login

项目注册游戏后台登录应用

$ vim gamesite/settings.py
INSTALLED_APPS = ['backend.login.apps.BackendConfig'
]

项目路由设置游戏后台应用路由

$ vim gamesite/urls.py
from django.contrib import admin
from django.urls import path,includeurlpatterns = [path('admin/', admin.site.urls),path('login/', include('backend.urls', namespace='backend'))
]

namespace

一个应用中可以创建多个URL,也就是说可以创建多个URL映射到同一个应用中。这样就会产生一个问题是,在反转解析URL时如果不使用命名空间,就会发生混淆,为了避免这个问题,可以使用实例命名空间,即在include函数中添加namespace即可。

创建游戏后台默认首页URL

$ vim backend/urls.py
from django.urls import path
from . import views
urlpatterns = [path("", views.index, name="home")
]

创建后台默认首页视图

$ vim backend/views.py
from django.http import HttpRequestdef index(request):return HttpResponse("backend index")

启动Django开发服务器测试

  • 使用Ctrl+C快捷键关闭服务器
  • 使用Ctrl+Z会将服务器进程挂起端口一直会被占用,重启后会提示端口占用。
$ python3 manage.py runserver 127.0.0.1:8000
  • Windows10查找指定端口运行的进程
$ netstat -ano | findstr 8000TCP    127.0.0.1:8000         0.0.0.0:0              LISTENING       32036TCP    127.0.0.1:8000         127.0.0.1:61366        ESTABLISHED     32036TCP    127.0.0.1:61366        127.0.0.1:8000         ESTABLISHED     24016TCP    127.0.0.1:61920        127.0.0.1:8000         TIME_WAIT       0TCP    127.0.0.1:61983        127.0.0.1:8000         TIME_WAIT       0
  • 强制/f杀死指定PID进程及其子进程/t
$ taskkill /f /t /pid 32036
成功: 已终止 PID 32036 (属于 PID 33980 子进程)的进程。

浏览器输入游戏后台首页地址测试

http://127.0.0.1:8000/backend/

管理后台

Dajango自动管理后台地址为http://127.0.0.1:8000/admin,使用前需使用管理员账号登录。

创建超级管理员账号

$ python3 manage.py createsuperuser

创建成功后会在auth_user表中生成一条记录,对管理员表进行进一步调整。创建的超级用户已经经过身份验证并拥有所有权限。

CREATE TABLE `auth_user` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',`username` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '用户名',`password` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '密码',`first_name` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',`last_name` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '姓氏',`email` varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '邮箱',`is_superuser` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否为超级管理员 0否 1是',`is_staff` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否员工 0否 1是',`is_active` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否激活 0否 1是',`date_joined` datetime(6) NOT NULL COMMENT '创建时间',`last_login` datetime(6) DEFAULT NULL COMMENT '最近登录时间',PRIMARY KEY (`id`),UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='管理员';

创建普通用户

用户登录

启用身份验证

当使用django-admin startproject命令创建项目是,所有必要配置都已完成,当第一次调用python3 manage.py migrate命令时会自动创建用户和权限的数据表。

身份验证的配置在项目配置文件settings.pyINSTALLED_APPSMIDDLEWAREA

$ vim settings.py
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','backend.apps.BackendConfig'
]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',
]

Django的django.contrib.auth.views自带的身份授权框架中内置了登录视图LoginView

创建用户登录URL调度器

  • 应用的URL调度器文件中使用app_name添加命名空间
  • 使用django.urls模块提供的re_path方法支持路径与转化器使用正则表达式
$ vim backend/urls.py
from django.contrib.auth.views import LoginView
from django.urls import path, re_path
from . import viewsapp_name = "backend"urlpatterns = [path('', views.index, name='home'),re_path(r'^login/$', LoginView.as_view(template_name='login/index.html'), name='login')
]

app_name

一个项目下的多个应用中可能存在定义同名的URL,为了避免反转解析URL时出现的混淆问题,Django提供了为应用添加命名空间的方式来区分URL,使用的方式是在urls.py中添加app_name来命名当前URL所属的应用名称。简单来说app_name的作用是使用应用命名空间来区分不同应用的URL。

使用视图函数views时Django会在URL解析完成后直接将request对象及URL解析器捕获的参数,比如使用re_path中正则捕获的未知参数或关键字丢给基于函数的视图。但在基于类的视图中,这些参数不能直接丢给一个类,因此就产生了as_view函数,as_view只做一件事儿就是返回一个闭包,这个闭包和视图函数views一样能够接收URL解析器传递过来的参数。

pathre_path源码中可以发现,它们都是partial类的实例,因此pathre_path并不是普通函数而是对象。

path = partial(_path, Pattern=RoutePattern)
re_path = partial(_path, Pattern=RegexPattern)

pathre_path执行逻辑

当启动Django项目时程序执行到urlpatterns位置,urlpatterns列表中各项依次得到执行,由于re_pathpath都是对象,当对象像函数一样调用时,其实是在调用对象的__call__方法,执行的结果是每个pathre_path调用都会返回一个URLPattern类的实例对象(django.urls.resolves.URLPattern)。

class URLPattern:def __init__(self, pattern, callback, default_args=None, name=None):self.pattern = patternself.callback = callbackself.default_args = default_args or {}self.name = name

URLPattern类的__init__方法中的各个参数基本对应了传入的pathre_path参数,callback属性包含了回调函数的引用。pathre_path执行时自身传入的第二个参数是as_view()立即执行函数,注意是as_view()函数而非as_view,此时as_view()会立即执行。as_view()执行完毕会返回一个闭包,因此callback中保存的实际上是这个闭包的引用。需要注意的是as_view()函数只会执行依次,即在Django项目启动后,之后所有请求的处理都是由as_view返回的闭包,即URLPattern实例对象中的callback回调函数执行。

当每次请求来临时,URL解析器首先会完成对URL的解析以匹配到相应的回调函数,然后立即去执行。

创建登录视图

  • 前端CSS采用TailwindCss
  • 前端JS采用AlpineJS

前端UI界面模板

$ vim backend/templates/login/index.html

浏览器访问游戏管理平台URL

http://127.0.0.1:8000/backend/login

游戏管理平台登录界面

模板form使用反向解析定义action

{% url 'backend:login' %}

为什么需要使用反向解析URL呢?

随着功能的增加会出现更多地视图,可能之前配置的正则表达式不够准确,于是就需要修改URL的正则表达式。但是正则表达式一旦修改,之前与之对应的超链接都需要重新修改,这是一件非常繁琐且容易遗漏的操作。有没有办法让连接根据正则表达式动态生成呢?这是就出现了反向解析。

反向解析主要用于模板中的超链接和视图中的重定向。如何使用反向解析呢?首先需要在定义URL时为include定义namespace命名空间,为url定义name别名属性。在模板中使用url标签时,在视图中利用reverse函数根据正则表达式动态生成地址,以降低后期维护成本。

后台首页

配置URL

$ vim backend/urls.py
from django.contrib.auth.views import LoginView
from django.urls import path, re_path
from . import viewsapp_name = "backend"urlpatterns = [path('', views.index, name='home'),
]

配置视图

$ vim backend/views.py
from django.shortcuts import renderdef index(request):return render(request, template_name='home/index.html')

配置模板

$ vim backend/templates/home/index.html

后台首页

登录处理

Django内置用户认证系统django.contrib.auth模块,使用默认创建的auth_user表来存储登录用户数据。

登录处理需使用auth模块的处理方法

authenticate()

authenticate方法提供了用户认证功能,即验证用户名username和密码password是否正确。

user = auth.authenticate(username="user", password="pwd")

authenticate方法如果认证成功会返回User对象,若查询失败则返回None

login(HttpRequest, user)

login方法接受一个HttpRequest对象以及一个经过认证的User对象。

auth.login(request, user)

auto.login方法执行会做两件事儿

  1. 完成会话操作,将用户数据保存到数据库,并生成随机sessionid保存到cookie中发送给客户端。
  2. 将验证后的user用户对象保存到request请求对象的request.user属性中

只要使用auth.login(request, user)登录操作后,后续即可从request.user拿到当前登录的用户对象。否则request.user得到的是一个匿名用户对象AnonymouseUser ObjectAnonymouseUserrequest.user的默认值。

logout(request)

logout函数接收一个HttpRequest请求对象,无返回值。当调用logout函数时当前请求的session会话信息会全部清。也就是说即使没有登录,执行logout函数也不会报错。

User

request.user.is_authenticated()

authenticate方法判断当前user是不是一个真正的User对象,用于检查用户是否已经通过认证,若通过返回True否则返回False

通过认证并不意味着用户拥有任何权限,甚至不会检查用户是否处于激活状态,只是表名用户成功的通过了认证。

为方便判断用户通过认证,auth模块提供了一个装饰器工具@login_required,用来快捷地给某个视图添加登录检测。

@login_required
def home(request):return redirect("login")

request.user.is_authenticated()出错误错误

'bool' object is not callable

错误原因是应该使用request.user.is_authenticated访问属性,而非使用方法访问。

登录流程

  1. 进入登录页面用户输入用户名和密码提交登录表单
  2. 用户登录视图接收到POST过来的用户名和密码并认证判断
  3. 认证判断成功后执行登录操作
  4. 登录成功重定向到首页,登录失败返回登录页面并携带错误提示。
  5. 首页需要判断当前用户是否已经登录,若已经登录则渲染视图,否则跳转安全退出。

编写登录URL规则

$ vim backend/urls.py
from django.urls import path, re_path
from . import viewsapp_name = "backend"urlpatterns = [path('', views.index, name='home'),re_path(r'^login/$', views.Login.as_view(), name="login"),re_path(r'^logout/$', views.logout, name="logout")
]

这里提供了三个URL地址分别是

  • http://127.0.0.1:8000/backend/ 首页
  • http://127.0.0.1:8000/backend/login 登录
  • http://127.0.0.1:8000/backend/logout 退出

编写登录视图

$ vim backend/views.py
from django.contrib import auth
from django.shortcuts import render, redirect
from django.views.generic.base import View# 首页
def index(request):# 判断用户是否登录if(request.user.is_authenticated == False):return redirect("backend:logout")# 渲染模板return render(request, template_name='home/index.html')# 登录
class Login(View):def get(self, request):return render(request, "login/index.html")def post(self, request):username = request.POST.get("username", None)password = request.POST.get("password", None)# 输入判断if(username == None or password == None):return render(request, "login/index.html", {"error":"请填写账号或密码"})# 使用auth模块去auth_user表查找result = auth.authenticate(username=username, password=password)if result==None:return render(request, "login/index.html", {"error": "账号或密码输入有误"})# 执行登录auth.login(request, result)# 跳转首页return redirect("backend:home")# 退出
def logout(request):auth.logout(request)return redirect("backend:login")

编写登录模板

$ vim backend/templates/login/index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>GM游戏管理平台</title><link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet"><link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"><script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
</head>
<body><div class="h-screen flex flex-col items-center md:flex-row"><div class="w-full h-screen bg-black hidden lg:block md:w-1/2 xl:w-2/3"><img src="https://source.unsplash.com/1441x768" alt="" class="w-full h-full object-cover object-center"></div><div class="w-full h-screen px-6 bg-white flex items-center justify-center  md:max-w-md md:mx-auto md:mx-0 md:w-1/2 lg:max-w-full lg:px-16 xl:w-1/3 xl:px-12"><div class="w-full h-100"><h1 class="text-xl font-bold text-center md:text-2xl"><i class="fa fa-android"></i> GM游戏管理平台</h1>{% if error %}<h2 class="text-xl leading-tight mt-12">{{ error }}</h2>{% else %}<h2 class="text-xl leading-tight mt-12">欢迎使用,请输入您的账号!</h2>{% endif %}<form action="{% url 'backend:login' %}" method="post" class="mt-6">{% csrf_token %}<label class="block text-gray-700">账号</label><input type="text"name="username"id="username"class="w-full px-4 py-3 mt-2 bg-gray-200 border rounded-lg focus:border-blue-500 focus:bg-white focus:outline-none"autofocus autocomplete required/><label class="block mt-4 text-gray-700">密码</label><input type="password"name="password"id="password"class="w-full px-4 py-3 mt-2 bg-gray-200 border rounded-lg focus:border-blue-500 focus:bg-white focus:outline-none"required/><div class="mt-2 text-right"><a href="" class="text-sm text-gray-799 focus:text-blue-700 hover:text-blue-700">忘记密码</a></div><button type="submit" class="block w-full px-4 py-3 mt-6 bg-blue-500 rounded-lg text-white focus:bg-blue-400 hover:bg-blue-400">登录</button></form><hr class="my-6 w-full border-gray-300"><button class="block w-full px-4 py-3 bg-white border border-gray-300 rounded-lg text-gray-900 focus:bg-gray-100 hover:bg-gray-100"><div class="flex items-center justify-center"><i class="fa fa-wechat"></i><span class="ml-2">微信登录</span></div></button><div class="mt-12 text-sm text-gray-500 text-center">&copy; 2020 JunChow - All Rights Reserved.</div></div></div></div>
</body>
</html>

编写首页视图

$ vim backend/templates/home/index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>GM游戏管理平台</title><link href="https://cdn.bootcdn.net/ajax/libs/tailwindcss/1.4.6/tailwind.min.css" rel="stylesheet"><script src="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.13.0/js/all.min.js"></script><script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
</head>
<body>
<!--container-->
<div class="mx-auto bg-gray-100" x-data="{dropdown:false, aside:true, menu:false}"><!--screen--><div class="min-h-screen flex flex-col"><!--header--><header class="relative bg-black text-white flex items-center justify-between px-4 py-1"><!--logo--><div class="inline-flex items-center"><i class="fas fa-bars" @click="aside=!aside"></i></div><!--avatar--><div class="flex flex-row items-center justify-center"><img src="http://source.unsplash.com/100x100/?avatar" class="h-8 h-8 rounded-full"><span class="p-2 hidden md:block text-xs">Admin</span><i class="fas fa-caret-down" @click="dropdown=!dropdown"></i></div><!--downdrop--><div class="absolute right-0 mt-16 mr-2 bg-white border rounded shadow-xl" x-show="dropdown"><ul class="list-reset divide-y text-gray-700 text-xs"><li><a href="" class="no-underline block px-4 py-2 hover:bg-gray-100">个人资料</a></li><li><a href="{% url 'backend:logout' %}" class="no-underline block px-4 py-2 hover:bg-gray-100">安全退出</a></li></ul></div></header><!--main--><main class="flex-1 flex"><!--mini--><nav class="p-2 bg-black text-white flex flex-col items-center justify-star" x-show="!aside"><span class="mb-2 last:mb-0 w-8 h-8 rounded-full hover:bg-gray-900 flex items-center justify-center" @click="aside=!aside"><i class="fas fa-users"></i></span><span class="mb-2 last:mb-0 w-8 h-8 rounded-full hover:bg-gray-900 flex items-center justify-center" @click="aside=!aside"><i class="fas fa-cogs"></i></span></nav><!--sidebar--><aside class="bg-gray-900 text-white hidden md:block lg:block" x-show="aside"><!--nav--><ul class="list-reset flex flex-col divide-y divide-gray-900 text-gray-400"><li class="w-full h-full"><!--level1--><div class="px-2 py-3 flex items-center justify-between bg-gray-800 text-base"><a href="" class="no-underline block flex items-center"><i class="fas fa-bug"></i><span class="ml-1 w-48">一级菜单</span></a><i class="fas fa-angle-right" @click="menu=!menu"></i></div><!--level2--><ul class="list-reset flex flex-col divide-y divide-gray-800 text-sm" x-show="menu"><li class="w-full h-full"><div class="py-2 pl-4 pr-2 flex items-center justify-between"><a href="" class="no-underline block"><i class="fas fa-caret-right"></i><span class="ml-1">二级菜单</span></a><i class="fas fa-angle-right"></i></div></li><li class="w-full h-full"><div class="py-2 pl-4 pr-2 flex items-center justify-between text-gray-500"><a href="" class="no-underline block text-sm"><i class="fas fa-caret-right"></i><span class="ml-1">二级菜单</span></a><i class="fas fa-angle-right"></i></div></li></ul></li></ul></aside><!--content--><div class="flex-1 p-4 overflow-hidden"><!--card--><div class="my-2 border border-solid border-gray-200 rounded bg-white shadow-sm w-full"><div class="px-2 py-3 border-b border-gray-200 font-base">卡片标题</div><div class="p-4 text-sm">卡片内容</div></div></div></main><!--footer--><footer></footer></div>
</div>
</body>
</html>

图片验证码

  • 目标:为登录表单添加随机图片验证码

图片验证码

创建随机图片验证码

为了创建图片验证码需引入图片处理类用生成图片验证码,这里采用的Python中的pillow模块。

安装PIL模块

$ pip3 install pillow

生成随机图片验证码流程

  1. 创建画布,并指定画布尺寸与背景色。
  2. 创建画笔
  3. 创建字体,并随机设置字体。
  4. 随机循环生成字体并使用画笔绘制文本
  5. 随机循环生成干扰线并使用画笔绘制弧线
  6. 随机循环生成干扰点并使用画笔绘制点

字体保存位置

字体属于全局静态资源,在项目根目录下创建static文件夹用于保存全局静态资源文件,在static目录下创建fonts文件夹用于保存字体文件。

在游戏管理后台创建工具类

$ vim backend/utils.py
import random
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont# 自定义工具类
class Utils:# 生成图片验证码@staticmethoddef makeAuthImg(len=4, width=270, height=50):# 定义随机颜色生成函数def make_random_color(start=0, stop=255):r = random.randrange(start, stop)g = random.randrange(start, stop)b = random.randrange(start, stop)return (r, g, b)# 定义随机字符生成函数def make_random_char():return random.choice([str(random.randint(0, 9)),chr(random.randint(97, 122)),chr(random.randint(65, 90))])# 创建画布canvas = Image.new(mode="RGB",size=(width, height),color=make_random_color(218, 255))# 创建画笔draw = ImageDraw.Draw(canvas, mode="RGB")# 创建字体font = ImageFont.truetype(font="static/fonts/Coustard-Regular.ttf",size=random.randint(int(height/3), height-10))# 随机生成字符code = ""for i in range(len):char = make_random_char()x = i * width / 4 + random.randint(0, 30)y = random.randint(0, int(height/3))draw.text((x, y),char,make_random_color(128, 192),font)code += char# 随机干扰线for i in range(len):x = random.randint(0, int(width/6))y = random.randint(0, int(height/2))draw.arc((x, y, width-x, height-y),0,180,make_random_color(64, 128))# 随机干扰点for i in range(len*50):draw.point((random.randint(0, width), random.randint(0, height)),fill=make_random_color(0, 64))return canvas, code

创建URL

  • 通过URL地址http://127.0.0.1:8000/backend/authimg获取图片验证码
  • 注意直接访问地址返回的将是乱码需放在img标签使用
$ vim backend/urls.py
re_path(r'^authimg/$', views.authimg, name="authimg")

创建视图

$ vim backend/views.py
from io import BytesIO
from django.http import HttpResponsefrom backend.utils import Utils# 生成随机图片验证码
def authimg(request):fd = BytesIO()# 生成随机图片二维码im,code = Utils.makeAuthImg()# 保存图片格式im.save(fd, "PNG")# 保存验证码request.session["authcode"] = code# 生成图片return HttpResponse(fd.getvalue())

登录模板中添加图片二维码选项

  • 前端HTML添加点击图片更换img标签的src属性,为了保证每次请求不同,在URL后添加随机数以示区分。

图片二维码

$ vim backend/templates/login/index.html
<div class="flex flex-wrap mb-6 -mx-3"><div class="w-full md:w-1/2 px-3"><label for="authcode" class="block mb-2 text-gray-700 tracking-wide">验证码</label><input type="text"name="authcode"id="authcode"class="block w-full bg-gray-200 text-gray-700 appearance-none border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:border-gray-500"></div><div class="w-full md:w-1/2 px-3" ><p class="block text-gray-500 tracking-wide mb-2">点击图片更换</p><img src="{% url 'backend:authimg' %}"id="authimg"class="max-w-full h-auto border-none align-middle"onclick="this.src = '{% url 'backend:authimg' %}'+'?_='+Math.random()"/></div>
</div>

登录表单提交验证图片二维码

$ vim backend/views.py
# 登录
class Login(View):template_name = "login/index.html"def error(self, request, message=""):return render(request, self.template_name, {"error":message})def get(self, request):return render(request, self.template_name)def post(self, request):username = request.POST.get("username", None)password = request.POST.get("password", None)authcode = request.POST.get("authcode", None)print(username, password, authcode.lower(), request.session["authcode"])# 图片验证码if not authcode:return self.error(request, "请填写验证码")# 验证码判断if authcode.lower() != request.session["authcode"]:return self.error(request, "验证码输入有误")# 输入判断if not username or not password:return self.error(request, "请填写账号或密码")# 使用auth模块去auth_user表查找admin = auth.authenticate(username=username, password=password)if not admin:return self.error(request, "账号或密码输入有误")# 执行登录auth.login(request, admin)# 跳转首页return redirect("backend:home")

前端登录错误错误提示Alert组件

  • 使用Alphine.js处理点击错误关闭按钮隐藏提示栏

登录错误Alert提示

<div class="relative px-4 py-3 mt-12 bg-red-100 border border-red-400 rounded text-red-700" role="alert" x-data="{showAlert:true}" x-show="showAlert"><strong class="font-bold">温馨提示</strong><span class="block sm:inline">{{ error }}</span><span class="absolute top-0 bottom-0 right-0 px-4 py-3 cursor-pointer" @click="showAlert=false">&times;</span>
</div>

前端资源

  • Tailwindcss框架 https://www.tailwindcss.cn/
  • SVG字体图标 https://fontawesome.cc/

版本控制

  • 创建远程仓库 https://gitee.com/junchow/gmws.git
  • 在本地项目中创建本地仓库
$ cd gamesite
$ git init

在本地项目下使用git init命令后会在项目根目录下生成隐藏的.git文件夹

  • 上传所有代码到本地仓库
$ git add .
$ git commit -m "initial commit"
  • 关联本地仓库和远程仓库
$ git remote add origin https://gitee.com/junchow/gmws.git
$ git push origin master
To https://gitee.com/junchow/gmws.git! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'https://gitee.com/junchow/gmws.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

错误原因远程仓库与本地仓库不一致,这里由于远程仓库中存在.README.md文件而本地仓库并不存在,因此需要将远程仓库先pull拉下来,对齐后再提交。另外本地文件中由于IDE自身的.idea文件夹随时处于变动状态,需要将其设置为不提交到远程仓库中。

$ git pull origin master
warning: no common commits
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), done.
From https://gitee.com/junchow/gmws* branch            master     -> FETCH_HEAD* [new branch]      master     -> origin/master
fatal: refusing to merge unrelated histories

错误原因是由于本地仓库和远程仓库两个分支是两个不同的版本,具有不同的提交历史。

解决方案是添加--allow-unrelated-histories允许不相关的历史提交,强制合并。

$ git pull origin master --allow-unrelated-histories

如果使用--rebase参数

$ git pull origin master --rebase
error: Cannot pull with rebase: You have unstaged changes.

pull实际上是fetch + merge的操作即将远程仓库的更新合并到本地仓库,--rebase是取消本地仓库最近的commit并将它们接到更新后的版本库中。

之所以出现错误是由于git pull -rebase的工作机制是

  1. 将本地commit提交到本地仓库的内容,取出来放到暂存区(stash),此时本地工作区是干净的。
  2. 从远程拉取代码到本地,由于工作区是干净的,因此会参数冲突。
  3. 从暂存区将之前提交的内容取出来跟拉下来的代码合并

查看GIT状态

$ git status
On branch master
Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified:   .idea/workspace.xmlno changes added to commit (use "git add" and/or "git commit -a")

远程提交忽略文件.gitignore

.idea文件夹添加到GIT的.gitignore文件内,远程提交时不包含idea文件夹。这个做法的前提条件是当前远程分支中并不存在.idea文件,如果已经存在则本地设置并提交是无效的,因此需要先将本地仓库的.idea文件删除。

$ git rm -r --cached .idea
rm '.idea/dataSources.local.xml'
rm '.idea/dataSources.xml'
rm '.idea/dataSources/037855b3-43da-4cb1-834b-35fcad4afe26.xml'
rm '.idea/gamesite.iml'
rm '.idea/misc.xml'
rm '.idea/modules.xml'
rm '.idea/vcs.xml'
rm '.idea/workspace.xml'

本地项目添加.ignore文件

$ vim .gitignore
# Intellij Pycharm
.idea/

再次提交

git add . && git commit -m "local add gitignore" && git push
[master 186c6f5] local add gitignore9 files changed, 2 insertions(+), 1869 deletions(-)create mode 100644 .gitignoredelete mode 100644 .idea/dataSources.local.xmldelete mode 100644 .idea/dataSources.xmldelete mode 100644 .idea/dataSources/037855b3-43da-4cb1-834b-35fcad4afe26.xmldelete mode 100644 .idea/gamesite.imldelete mode 100644 .idea/misc.xmldelete mode 100644 .idea/modules.xmldelete mode 100644 .idea/vcs.xmldelete mode 100644 .idea/workspace.xml
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, usegit push --set-upstream origin master

错误The current branch master has no upstream branch.To push the current branch and set the remote as upstream表示没有将本地分支和远程仓库的分支进行关联。使用git pull默认会上传到origin下的master分支。

解决的方案有两种

第一种方式:保证远程分支存在的情况下,设置本地分支追踪远程分支。

$ git push --set-upstream origin master
Counting objects: 3, done.
Delta compression using up to 6 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 299 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Powered by GITEE.COM [GNK-5.0]
To https://gitee.com/junchow/gmws.gitd95be9b..186c6f5  master -> master
Branch master set up to track remote branch master from origin.D:\python\django\project\gamesite>git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

查看当前项目仓库的所有分支,红色表示远程分支,remotes/origin/master表示远程的主分支。

$ git branch -a
* masterremotes/origin/master

注意git中的origin表示关联或克隆远程仓库名称,git为你创建指向这个远程仓库的标签,它会指向repository

查看指向的repository

$ git remote -v
origin  https://gitee.com/junchow/gmws.git (fetch)
origin  https://gitee.com/junchow/gmws.git (push)

第二种方式:如果远程没有需要关联的分支则自动创建以实现关联

$ git push -u origin master
http://www.taodudu.cc/news/show-2938940.html

相关文章:

  • 后台管理系统模板简介
  • 扫描二维码实现后台管理系统登录
  • vue通用后台管理(登录页面)
  • 基于VUE的后台管理系统
  • 登录-后台管理系统
  • 后台管理系统项目-登录页-实现步骤
  • 中后台管理系统之登录流程
  • 后台系统登录一般流程
  • 如何回到后台管理系统登录页?
  • 后台管理系统中的登录页面
  • 后台管理系统登录思路
  • 后台管理系统——登录功能
  • Vue后台管理系统项目——实现登录功能
  • c# OutlookBar控件
  • DEFT: Detection Embeddings for Tracking论文解读
  • Leveraging Stereo-Camera Data for Real-Time Dynamic Obstacle Detection and Tracking
  • 【千锋Python2205班10.20笔记-day04-接口和常见反爬(一阶段)】
  • 以下未发布-Android的多线程以及异步消息处理机制,android移动开发基础案例教程源码
  • 持续学习+元学习+无监督学习文章调研(七)
  • 分享一个 C# Winfrom 下的 OutlookBar 控件的使用
  • 3GPP TS 29244-g30 中英文对照 | 5.2.2 Usage Reporting Rule Handling
  • MaskRNN Instance Level Video Object Segmentation 论文翻译
  • 论文阅读--Adapted Dynamic Memory Network for Emotion Recognition in Conversation
  • 基础篇. ARM架构和处理器(3)
  • HCE-OS基础介绍
  • (转载)JAVA小知识
  • 一篇就够!数据增强方法综述
  • 论文笔记:联邦学习——Federated Learning: Strategies for Improving Communication Efficiency
  • GUI(图形用户界面)——AWT概述、布局管理器
  • 嵌入式linux 系统支持usb wifi BL-R8723BT1

Django管理后台之登录相关推荐

  1. django管理后台修改管理系统名称和应用名称的方法

    1.django管理后台修改管理系统名称的方法 打开任何一个应用目录下的admin.py文件,在文件末尾添加代码: admin.site.site_header='这是一个网站后台管理系统' admi ...

  2. Django管理后台

    目录 第一种注册model的方法 认识后台管理页面 第二种注册model的方法 ModelAdmin的常用属性 利用actions丰富动作工具栏 利用list_display修改显示列 利用searc ...

  3. vue3-admin商品管理后台项目(登录页开发和功能实现)

    今天来实现vue3-admin商品管理后台项目的登录页功能 首先在pages文件夹下面添加一个login.vue文件,里面先写入简单的template <template><div& ...

  4. Bootstrap4+MySQL前后端综合实训-Day02-PM【新闻管理后台(登录页面、首页)、#left>a:nth-child(4) {}】

    [Bootstrap4前端框架+MySQL数据库]前后端综合实训[10天课程 博客汇总表 详细笔记] 目   录 主页 #left>a:nth-child(4) {} 登录页 主页 #left& ...

  5. django管理后台列表页,TextField字段展示换行等格式(format_html)

    因为在django后台管理页面中国,在详情页中输入字段后,在列表页只能一行显示,所以,最后的解决办法是: 在model中定义一个新字段,然后运用到了format_html 方法,把原本的字段retur ...

  6. 电商管理后台的登录流程

    **一 登录的流程 1.登录的form表单 <!-- ref="loginFormRef" 代表重置表单 --><el-form class="wzl_ ...

  7. Django mysql 多线程_【实例:利用Django管理后台管理IP地址】(四)Django test+多线程+数据库+(踩坑)...

    准备在views.py编写函数,多线程检测每个IP地址的占用情况.胡乱一通写完之后,哦豁,怎么测试写得对不对呢? 一开始想单独测试views.py文件,结果要引入各种包和配置文件,还要注意各种顺序,各 ...

  8. 商户管理后台/消费统计管理/云平台商户端管理后台原型/PaaS金融服务平台商户端管理后台原型/企业管理系统后台/账户管理/工单管理/充值管理/汇款单管理/余额管理/用户管理/认证管理/web后台原型

    商户管理后台/消费统计管理/云平台商户端管理后台原型/PaaS金融服务平台商户端管理后台原型/企业管理系统后台/账户管理/工单管理/充值管理/汇款单管理/余额管理/用户管理/认证管理/axure后台管 ...

  9. Django 3.2.5博客开发教程:用Admin管理后台管理数据

    上节我们我们把数据库迁移到数据库里去了,那么现在我们数据库里是个什么样的情况呢?我们点击Pycharm右上角的Database,然后在网站项目里选中我们的数据库文件db.sqlite3,把它拖到Dat ...

最新文章

  1. Git 常用操作(3)- 本地分之显示、创建、切换、合并和删除操作
  2. LVM学习之LVM基础
  3. 产品推广系统推荐乐云seo_优化推广公司红利产品推荐“爱采购cpc竞价版”
  4. iOS9定位获取经纬度 swift
  5. 在docker container中为gsutil认证gcloud
  6. JavaWeb——springMVC请求数据绑定分类解析(深度好文)
  7. 超详细 Spring @RequestMapping 注解使用技巧
  8. 在sql中如何插入数据
  9. linux php连接mysql权限不够_PHP LINUX APACHE MYSQL权限挣扎
  10. SpringMVC和Structs2
  11. 使用IDEA进行Lua代码调试、自动提示、代码跳转、智能重命名
  12. 微信小程序毕业设计 基于微信共享小程序系统开题报告
  13. 摘录:《晨间日记的奇迹》
  14. 信息学奥赛一本通 1336:【例3-1】找树根和孩子
  15. 杂谈:微信推出赞赏码功能
  16. android修行之路----经典书籍
  17. 程序员的英文代号_构建一个代号为1的聊天应用程序4
  18. SCSI接口和SAS接口的区别
  19. H5页面制作功能真的很强大!
  20. 中国成为论文发表数量第一的国家

热门文章

  1. CSS3 弹性布局 flex 项目属性
  2. 微信公众号开发——关键词自动回复
  3. python sqlachemy模糊查询报错
  4. PreSonus Studio One 5 Professional v5.5.0 WiN-MAC 音乐制作宿主软件
  5. 安装程序出现2502、2503错误解决方法
  6. linux删除指定的行
  7. 矩阵分解——QR分解
  8. JavaWeb框架(一):Web入门,Http的请求和响应,https介绍,Web实战自定义服务器
  9. 毛巾架用久了生锈怎么办?
  10. QQ(微信)一次性发送多条信息(连续发520遍我爱你)