Django提供了一套身份驗證和授權(quán)的權(quán)限系統(tǒng),允許驗證用戶憑證窖式,并定義每個用戶允許執(zhí)行的操作。
權(quán)限系統(tǒng)框架包括了用戶和分組的內(nèi)置模型动壤,用于登錄用戶的權(quán)限萝喘,指定用戶是否可以執(zhí)行任務、表單琼懊、視圖阁簸,以及查看限制內(nèi)容的工具。
Django身份驗證系統(tǒng)為通用設計因此不提供其它Web身份驗證系統(tǒng)中所提供的功能哼丈,對于某些常見問題可作為第三方軟件包提供启妹,比如限制登錄嘗試和針對第三方的身份驗證
登錄授權(quán)
manage
應用路由設置后臺登錄URL
$ vim apps/manage/urls.py
from django.urls import path, re_path
from apps.manage.apps import ManageConfig
from .views import login, home
app_name = ManageConfig.name
urlpatterns = [
path("", home.index, name="home"),
]
隨著功能開發(fā)views.py
視圖文件中的代碼會越來越多,整個文件會越來越臃腫醉旦,不便于維護且不符合單一原則饶米。因此應該將views.py
中內(nèi)容拆分到同一個目錄下。
manage
應用下創(chuàng)建views
文件夾同時在該目錄下創(chuàng)建__init__.py
文件使其作為一個Python包package
髓抑。
$ mkdir -p apps/manage/views
$ touch apps/manage/views/__init__.py
在views
目錄下將功能類似的方法存到同一個文件下咙崎,這里針對登錄和首頁,兩個獨立的功能模塊劃分出兩個視圖文件吨拍。
$ vim apps/manage/views/home.py
$ vim apps/manage/views/login.py
在manage
應用的url.py
文件中使用from .views import login, home
導入當前目錄下views
包下的home
模塊和login
模塊褪猛。將登錄UI界面中涉及到操作放在login
模塊下,將后臺首頁UI界面中涉及到的操作放到home
模塊中羹饰。
path("", home.index, name="home")
同樣為了反轉(zhuǎn)解析URL伊滋,在URL的路由規(guī)則中添加name
為當前路由設置別名。
為方便測試在視圖中直接輸出字符串文本
$ vim apps/manage/views/home.py
from django.http import HttpResponse
def index(request):
return HttpResponse("home index")
運行測試
啟動Django開發(fā)服務器測試
- 使用
Ctrl+C
快捷鍵關(guān)閉服務器 - 使用
Ctrl+Z
會將服務器進程掛起端口一直會被占用队秩,重啟后會提示端口占用笑旺。
$ python3 manage.py runserver 127.0.0.1:8000
- Windows10查找指定端口運行的進程
$ netstat -ano | findstr 8000
TCP 127.0.0.1:8000 0.0.0.0:0 LISTENING 32036
TCP 127.0.0.1:8000 127.0.0.1:61366 ESTABLISHED 32036
TCP 127.0.0.1:61366 127.0.0.1:8000 ESTABLISHED 24016
TCP 127.0.0.1:61920 127.0.0.1:8000 TIME_WAIT 0
TCP 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/manage/
項目管理后臺
Dajango自動管理后臺地址為http://127.0.0.1:8000/admin
馍资,使用前需使用管理員賬號登錄筒主。
創(chuàng)建超級管理員賬號
$ python3 manage.py createsuperuser
創(chuàng)建成功后會在auth_user
表中生成一條記錄,對管理員表進行進一步調(diào)整。創(chuàng)建的超級用戶已經(jīng)經(jīng)過身份驗證并擁有所有權(quán)限乌妙。
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 '創(chuàng)建時間',
`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='管理員';
內(nèi)置身份驗證
當使用django-admin startproject
命令創(chuàng)建項目是使兔,所有必要配置都已完成,當?shù)谝淮握{(diào)用python3 manage.py migrate
命令時會自動創(chuàng)建用戶和權(quán)限的數(shù)據(jù)表藤韵。
身份驗證的配置在項目配置文件settings.py
的INSTALLED_APPS
和MIDDLEWAREA
中
$ vim settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Django的django.contrib.auth.views
自帶的身份授權(quán)框架中內(nèi)置了登錄視圖LoginView
創(chuàng)建用戶登錄URL調(diào)度器
- 應用的URL調(diào)度器文件中使用
app_name
添加命名空間 - 使用
django.urls
模塊提供的re_path
方法支持路徑與路徑轉(zhuǎn)化器使用正則表達式
$ vim apps/manage/urls.py
from django.urls import path, re_path
from apps.manage.apps import ManageConfig
from .views import login, home
app_name = ManageConfig.name
urlpatterns = [
path("", home.index, name="home"),
re_path(r"^login/$", login.Logon.as_view(), name="login"),
re_path(r"^authimg/$", login.authimg, name="authimg"),
re_path(r"^logout/$", home.logout, name="logout"),
]
app_name
一個項目下的多個應用中可能存在定義同名的URL虐沥,為了避免反轉(zhuǎn)解析URL時出現(xiàn)的混淆問題,Django提供了為應用添加命名空間的方式來區(qū)分URL泽艘,使用的方式是在urls.py
中添加app_name
來命名當前URL所屬的應用名稱欲险。簡單來說app_name
的作用是使用應用命名空間來區(qū)分不同應用的URL。
使用視圖函數(shù)views
時Django會在URL解析完成后直接將request
對象及URL解析器捕獲的參數(shù)匹涮,比如使用re_path
中正則捕獲的未知參數(shù)或關(guān)鍵字丟給基于函數(shù)的視圖天试。但在基于類的視圖中,這些參數(shù)不能直接丟給一個類焕盟,因此就產(chǎn)生了as_view
函數(shù)秋秤,as_view
只做一件事兒就是返回一個閉包,這個閉包和視圖函數(shù)views
一樣能夠接收URL解析器傳遞過來的參數(shù)脚翘。
path
從path
和re_path
源碼中可以發(fā)現(xiàn)灼卢,它們都是partial
類的實例,因此path
和re_path
并不是普通函數(shù)而是對象来农。
path = partial(_path, Pattern=RoutePattern)
re_path = partial(_path, Pattern=RegexPattern)
path
和re_path
執(zhí)行邏輯
當啟動Django項目時程序執(zhí)行到urlpatterns
位置鞋真,urlpatterns
列表中各項依次得到執(zhí)行,由于re_path
和path
都是對象沃于,當對象像函數(shù)一樣調(diào)用時涩咖,其實是在調(diào)用對象的__call__
方法,執(zhí)行的結(jié)果是每個path
或re_path
調(diào)用都會返回一個URLPattern
類的實例對象(django.urls.resolves.URLPattern
)繁莹。
class URLPattern:
def __init__(self, pattern, callback, default_args=None, name=None):
self.pattern = pattern
self.callback = callback
self.default_args = default_args or {}
self.name = name
URLPattern
類的__init__
方法中的各個參數(shù)基本對應了傳入的path
或re_path
參數(shù)檩互,callback
屬性包含了回調(diào)函數(shù)的引用。path
或re_path
執(zhí)行時自身傳入的第二個參數(shù)是as_view()
立即執(zhí)行函數(shù)咨演,注意是as_view()
函數(shù)而非as_view
闸昨,此時as_view()
會立即執(zhí)行。as_view()
執(zhí)行完畢會返回一個閉包薄风,因此callback
中保存的實際上是這個閉包的引用饵较。需要注意的是as_view()
函數(shù)只會執(zhí)行依次,即在Django項目啟動后遭赂,之后所有請求的處理都是由as_view
返回的閉包循诉,即URLPattern
實例對象中的callback
回調(diào)函數(shù)執(zhí)行。
當每次請求來臨時撇他,URL解析器首先會完成對URL的解析以匹配到相應的回調(diào)函數(shù)茄猫,然后立即去執(zhí)行狈蚤。
內(nèi)置登錄處理
Django內(nèi)置用戶認證系統(tǒng)django.contrib.auth
模塊,使用默認創(chuàng)建的auth_user
表來存儲登錄用戶數(shù)據(jù)募疮。
登錄處理需使用auth
模塊的處理方法
authenticate()
authenticate
方法提供了用戶認證功能炫惩,即驗證用戶名username
和密碼password
是否正確。
user = auth.authenticate(username="user", password="pwd")
authenticate
方法如果認證成功會返回User
對象阿浓,若查詢失敗則返回None
。
login(HttpRequest, user)
login
方法接受一個HttpRequest
對象以及一個經(jīng)過認證的User
對象蹋绽。
auth.login(request, user)
auto.login
方法執(zhí)行會做兩件事兒
- 完成會話操作芭毙,將用戶數(shù)據(jù)保存到數(shù)據(jù)庫,并生成隨機
sessionid
保存到cookie
中發(fā)送給客戶端卸耘。 - 將驗證后的
user
用戶對象保存到request
請求對象的request.user
屬性中
只要使用auth.login(request, user)
登錄操作后退敦,后續(xù)即可從request.user
拿到當前登錄的用戶對象。否則request.user
得到的是一個匿名用戶對象AnonymouseUser Object
蚣抗,AnonymouseUser
是request.user
的默認值侈百。
logout(request)
logout
函數(shù)接收一個HttpRequest
請求對象,無返回值翰铡。當調(diào)用logout
函數(shù)時當前請求的session
會話信息會全部清钝域。也就是說即使沒有登錄,執(zhí)行logout
函數(shù)也不會報錯锭魔。
User
request.user.is_authenticated()
authenticate
方法判斷當前user
是不是一個真正的User
對象例证,用于檢查用戶是否已經(jīng)通過認證,若通過返回True
否則返回False
迷捧。
通過認證并不意味著用戶擁有任何權(quán)限织咧,甚至不會檢查用戶是否處于激活狀態(tài),只是表名用戶成功的通過了認證漠秋。
為方便判斷用戶通過認證笙蒙,auth
模塊提供了一個裝飾器工具@login_required
,用來快捷地給某個視圖添加登錄檢測庆锦。
@login_required
def home(request):
return redirect("login")
request.user.is_authenticated()
出錯誤錯誤
'bool' object is not callable
錯誤原因是應該使用request.user.is_authenticated
訪問屬性捅位,而非使用方法訪問。
前端模板
- 前端圖標采用SVG類型的字體圖標 FontAwesome
<script src="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.13.0/js/all.min.js"></script>
- 前端CSS采用 TailwindCSS
<link rel="stylesheet">
- 前端JS采用 AlpineJS
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
前端資源
- Tailwindcss框架 https://www.tailwindcss.cn/
- SVG字體圖標 https://fontawesome.cc/
- 前端圖片 https://source.unsplash.com
用戶登錄
用戶登錄流程
- 進入登錄頁面用戶輸入用戶名和密碼提交登錄表單
- 用戶登錄視圖接收到POST過來的用戶名和密碼并認證判斷
- 認證判斷成功后執(zhí)行登錄操作
- 登錄成功重定向到首頁肥荔,登錄失敗返回登錄頁面并攜帶錯誤提示绿渣。
- 首頁需要判斷當前用戶是否已經(jīng)登錄,若已經(jīng)登錄則渲染視圖燕耿,否則跳轉(zhuǎn)安全退出中符。
編寫登錄URL規(guī)則
$ vim apps/manage/urls.py
from django.urls import path, re_path
from apps.manage.apps import ManageConfig
from .views import login, home
app_name = ManageConfig.name
urlpatterns = [
re_path(r"^login/$", login.Login.as_view(), name="login"),
re_path(r"^authimg/$", login.authimg, name="authimg"),
]
這里提供了URL地址分別是
編寫登錄視圖
$ vim apps/manage/views/login.py
from io import BytesIO
from django.contrib import auth
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.views.generic.base import View
from apps.manage.utils import Utils
# 登錄
class Login(View):
template_name = "login.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, "請?zhí)顚戲炞C碼")
# 驗證碼判斷
if authcode.lower() != request.session["authcode"]:
return self.error(request, "驗證碼輸入有誤")
# 輸入判斷
if not username or not password:
return self.error(request, "請?zhí)顚戀~號或密碼")
# 使用auth模塊去auth_user表查找
user = auth.authenticate(username=username, password=password)
if not user:
return self.error(request, "賬號或密碼輸入有誤")
# 執(zhí)行登錄
auth.login(request, user)
# 跳轉(zhuǎn)首頁
return redirect("manage:home")
# 生成隨機圖片驗證碼
def authimg(request):
fd = BytesIO()
# 生成隨機圖片二維碼
im,code = Utils.makeAuthImg()
# 保存圖片格式
im.save(fd, "PNG")
# 保存驗證碼 統(tǒng)一轉(zhuǎn)化為小寫
request.session["authcode"] = code.lower()
# 生成圖片
return HttpResponse(fd.getvalue())
為什么需要使用反向解析URL呢?
redirect("manage:home")
隨著功能的增加會出現(xiàn)更多地視圖誉帅,可能之前配置的正則表達式不夠準確淀散,于是就需要修改URL的正則表達式右莱。但是正則表達式一旦修改,之前與之對應的超鏈接都需要重新修改档插,這是一件非常繁瑣且容易遺漏的操作慢蜓。有沒有辦法讓連接根據(jù)正則表達式動態(tài)生成呢?這是就出現(xiàn)了反向解析郭膛。
反向解析主要用于模板中的超鏈接和視圖中的重定向晨抡。如何使用反向解析呢?首先需要在定義URL時為include
定義namespace
命名空間则剃,為url
定義name
別名屬性耘柱。在模板中使用url
標簽時,在視圖中利用reverse
函數(shù)根據(jù)正則表達式動態(tài)生成地址棍现,以降低后期維護成本调煎。
編寫登錄模板
$ vim apps/manage/templates/login.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 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>
<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" 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="fas fa-dragon"></i> GM游戲管理平臺</h1>
{% if error %}
<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">×</span>
</div>
{% else %}
<h2 class="text-xl leading-tight mt-12">歡迎使用,請輸入您的賬號和密碼己肮!</h2>
{% endif %}
<form action="{% url 'manage:login' %}" method="post" class="mt-6">
{% csrf_token %}
<label class="block text-gray-700" for="username">賬號</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" for="password">密碼</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>
<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 px-4 py-3 mb-3 leading-tight bg-gray-200 appearance-none border border-gray-200 rounded-lg focus:outline-none focus:bg-white focus:border-blue-500"
required/>
</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 'manage:authimg' %}"
class="max-w-full h-auto border-none align-middle"
onclick="this.src = '{% url 'manage:authimg' %}'+'?_='+Math.random()"
/>
</div>
</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">
<span class="ml-2">微信登錄</span>
</div>
</button>
<div class="mt-12 text-sm text-gray-500 text-center">© 2020 JunChow - All Rights Reserved.</div>
</div>
</div>
</div>
</body>
</html>
圖片驗證碼
目標:為登錄表單添加隨機圖片驗證碼
創(chuàng)建隨機圖片驗證碼
為了創(chuàng)建圖片驗證碼需引入圖片處理類用生成圖片驗證碼士袄,這里采用的Python中的pillow
模塊。
安裝PIL模塊
$ pip3 install pillow
生成隨機圖片驗證碼流程
- 創(chuàng)建畫布谎僻,并指定畫布尺寸與背景色娄柳。
- 創(chuàng)建畫筆
- 創(chuàng)建字體,并隨機設置字體戈稿。
- 隨機循環(huán)生成字體并使用畫筆繪制文本
- 隨機循環(huán)生成干擾線并使用畫筆繪制弧線
- 隨機循環(huán)生成干擾點并使用畫筆繪制點
字體保存位置
字體屬于全局靜態(tài)資源西土,在項目根目錄下創(chuàng)建static
文件夾用于保存全局靜態(tài)資源文件,在static
目錄下創(chuàng)建fonts
文件夾用于保存字體文件鞍盗。
創(chuàng)建工具類
$ vim apps/manage/utils.py
import random
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
# 自定義工具類
class Utils:
# 生成圖片驗證碼
@staticmethod
def makeAuthImg(len=4, width=270, height=50):
# 定義隨機顏色生成函數(shù)
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)
# 定義隨機字符生成函數(shù)
def make_random_char():
return random.choice([
str(random.randint(0, 9)),
chr(random.randint(97, 122)),
chr(random.randint(65, 90))
])
# 創(chuàng)建畫布
canvas = Image.new(
mode="RGB",
size=(width, height),
color=make_random_color(218, 255)
)
# 創(chuàng)建畫筆
draw = ImageDraw.Draw(canvas, mode="RGB")
# 創(chuàng)建字體
font = ImageFont.truetype(
font="static/fonts/Mogul-Arial.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
創(chuàng)建URL
- 通過URL地址
http://127.0.0.1:8000/manage/authimg
獲取圖片驗證碼 - 注意直接訪問地址返回的將是亂碼需放在
img
標簽使用
$ vim apps/manage/urls.py
re_path(r"^authimg/$", login.authimg, name="authimg")
創(chuàng)建視圖
$ vim apps/manage/views/login.py
from io import BytesIO
from django.http import HttpResponse
from apps.manage.utils import Utils
# 生成隨機圖片驗證碼
def authimg(request):
fd = BytesIO()
# 生成隨機圖片二維碼
im,code = Utils.makeAuthImg()
# 保存圖片格式
im.save(fd, "PNG")
# 保存驗證碼 統(tǒng)一轉(zhuǎn)化為小寫
request.session["authcode"] = code.lower()
# 生成圖片
return HttpResponse(fd.getvalue())
登錄模板中添加圖片驗證碼選項
- 前端HTML添加點擊圖片更換
img
標簽的src
屬性需了,為了保證每次請求不同,在URL后添加隨機數(shù)以示區(qū)分般甲。
$ vim apps/manage/templates/login.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 'manage:authimg' %}'+'?_='+Math.random()"
/>
</div>
</div>
登錄表單提交
$ vim apps/manage/views/login.py
from io import BytesIO
from django.contrib import auth
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.views.generic.base import View
from apps.manage.utils import Utils
# 登錄
class Login(View):
template_name = "login.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, "請?zhí)顚戲炞C碼")
# 驗證碼判斷
if authcode.lower() != request.session["authcode"]:
return self.error(request, "驗證碼輸入有誤")
# 輸入判斷
if not username or not password:
return self.error(request, "請?zhí)顚戀~號或密碼")
# 使用auth模塊去auth_user表查找
user = auth.authenticate(username=username, password=password)
if not user:
return self.error(request, "賬號或密碼輸入有誤")
# 執(zhí)行登錄
auth.login(request, user)
# 跳轉(zhuǎn)首頁
return redirect("manage:home")
前端登錄錯誤錯誤提示Alert組件
- 使用
Alphine.js
處理點擊錯誤關(guān)閉按鈕隱藏提示欄
<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">×</span>
</div>
后臺首頁
配置首頁路由
$ vim apps/manage/urls.py
from django.urls import path, re_path
from apps.manage.apps import ManageConfig
from .views import login, home
app_name = ManageConfig.name
urlpatterns = [
path("", home.index, name="home"),
re_path(r"^logout/$", home.logout, name="logout"),
]
創(chuàng)建首頁視圖完成首頁登錄驗證
$ vim apps/manage/views/home.py
from django.contrib import auth
from django.shortcuts import render, redirect
# 首頁
def index(request):
template_name = 'home.html'
print(request.user.is_authenticated)
# 判斷用戶是否登錄
if(request.user.is_authenticated == False):
return redirect("manage:logout")
# 渲染模板
return render(request, template_name)
# 退出
def logout(request):
auth.logout(request)
return redirect("manage:login")
創(chuàng)建模板
$ vim apps/manage/templates/home.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 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">卡片內(nèi)容</div>
</div>
</div>
</main>
<!--footer-->
<footer></footer>
</div>
</div>
</body>
</html>
由于后臺頁面存在多個高度復用的區(qū)塊肋乍,在下一步會將模板進行抽象提取出公共部分,使用模板導入和模板加載的方式敷存,將拆分出模板碎片有機地整合在一起墓造,以增加模板文件代碼的復用。
版本控制
- 創(chuàng)建遠程倉庫 https://gitee.com/junchow/gmws.git
- 在本地項目中創(chuàng)建本地倉庫
$ cd gamesite
$ git init
在本地項目下使用git init
命令后會在項目根目錄下生成隱藏的.git
文件夾
- 上傳所有代碼到本地倉庫
$ git add .
$ git commit -m "initial commit"
- 關(guān)聯(lián)本地倉庫和遠程倉庫
$ 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
文件夾隨時處于變動狀態(tài)蛉拙,需要將其設置為不提交到遠程倉庫中。
$ 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
允許不相關(guān)的歷史提交吮廉,強制合并。
$ git pull origin master --allow-unrelated-histories
如果使用--rebase
參數(shù)
$ git pull origin master --rebase
error: Cannot pull with rebase: You have unstaged changes.
pull
實際上是fetch + merge
的操作即將遠程倉庫的更新合并到本地倉庫畸肆,--rebase
是取消本地倉庫最近的commit
并將它們接到更新后的版本庫中宦芦。
之所以出現(xiàn)錯誤是由于git pull -rebase
的工作機制是
- 將本地
commit
提交到本地倉庫的內(nèi)容,取出來放到暫存區(qū)(stash)轴脐,此時本地工作區(qū)是干凈的调卑。 - 從遠程拉取代碼到本地,由于工作區(qū)是干凈的大咱,因此會參數(shù)沖突令野。
- 從暫存區(qū)將之前提交的內(nèi)容取出來跟拉下來的代碼合并
查看GIT狀態(tài)
$ 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.xml
no changes added to commit (use "git add" and/or "git commit -a")
遠程提交忽略文件.gitignore
將.idea
文件夾添加到GIT的.gitignore
文件內(nèi),遠程提交時不包含idea
文件夾徽级。這個做法的前提條件是當前遠程分支中并不存在.idea
文件,如果已經(jīng)存在則本地設置并提交是無效的聊浅,因此需要先將本地倉庫的.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 gitignore
9 files changed, 2 insertions(+), 1869 deletions(-)
create mode 100644 .gitignore
delete mode 100644 .idea/dataSources.local.xml
delete mode 100644 .idea/dataSources.xml
delete mode 100644 .idea/dataSources/037855b3-43da-4cb1-834b-35fcad4afe26.xml
delete mode 100644 .idea/gamesite.iml
delete mode 100644 .idea/misc.xml
delete mode 100644 .idea/modules.xml
delete mode 100644 .idea/vcs.xml
delete 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, use
git push --set-upstream origin master
錯誤The current branch master has no upstream branch.To push the current branch and set the remote as upstream
表示沒有將本地分支和遠程倉庫的分支進行關(guān)聯(lián)。使用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.git
d95be9b..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
* master
remotes/origin/master
注意git
中的origin
表示關(guān)聯(lián)或克隆遠程倉庫名稱强重,git
為你創(chuàng)建指向這個遠程倉庫的標簽绞呈,它會指向repository
。
查看指向的repository
$ git remote -v
origin https://gitee.com/junchow/gmws.git (fetch)
origin https://gitee.com/junchow/gmws.git (push)
第二種方式:如果遠程沒有需要關(guān)聯(lián)的分支則自動創(chuàng)建以實現(xiàn)關(guān)聯(lián)
$ git push -u origin master