django+xadmin在线教育平台(十二)
6-4 用form實(shí)現(xiàn)登錄-1
上面我們的用戶登錄的方法是基于函數(shù)來做的。本節(jié)我們做一個(gè)基于類方法的版本。
要求對類的繼承有了解。
基礎(chǔ)教程中基本上都是基于函數(shù)來做的,其實(shí)更推薦基于類來做。基于類可以帶來不少好處
# 基于類實(shí)現(xiàn)需要繼承的view from django.views.generic.base import View class LoginView(View): # 直接調(diào)用get方法免去判斷 def get(self, request): # render就是渲染html返回用戶 # render三變量: request 模板名稱 一個(gè)字典寫明傳給前端的值 return render(request, "login.html", {}) def post(self, request): # 取不到時(shí)為空,username,password為前端頁面name值 user_name = request.POST.get("username", "") pass_word = request.POST.get("password", "") # 成功返回user對象,失敗返回null user = authenticate(username=user_name, password=pass_word) # 如果不是null說明驗(yàn)證成功 if user is not None: # login_in 兩參數(shù):request, user # 實(shí)際是對request寫了一部分東西進(jìn)去,然后在render的時(shí)候: # request是要render回去的。這些信息也就隨著返回瀏覽器。完成登錄 login(request, user) # 跳轉(zhuǎn)到首頁 user request會(huì)被帶回到首頁 return render(request, "index.html") # 沒有成功說明里面的值是None,并再次跳轉(zhuǎn)回主頁面 else: return render(request, "login.html", {"msg": "用戶名或密碼錯(cuò)誤! "}) mark繼承的view中的方法。
django1.9.8 urls中的配置:
# 換用類實(shí)現(xiàn) from users.views import LoginView# 基于類方法實(shí)現(xiàn)登錄,這里是調(diào)用它的方法 url('^login/$', LoginView.as_view(), name="login")Django2.0.1 urls配置:
# 基于類方法實(shí)現(xiàn)登錄,這里是調(diào)用它的方法path('login/', LoginView.as_view(), name="login")6-5 form字段驗(yàn)證
驗(yàn)證最大長度,是否為空等一系列。
users下新建forms文件。
# encoding: utf-8 __author__ = 'mtianyan' __date__ = '2018/1/10 0010 04:44' # 引入Django表單 from django import forms # 登錄表單驗(yàn)證 class LoginForm(forms.Form): # 用戶名密碼不能為空 username = forms.CharField(required=True) password = forms.CharField(required=True, min_length=5)定義好forms之后我們來使用它做驗(yàn)證。
def post(self, request): # 類實(shí)例化需要一個(gè)字典參數(shù)dict:request.POST就是一個(gè)QueryDict所以直接傳入 # POST中的usernamepassword,會(huì)對應(yīng)到form中 login_form = LoginForm(request.POST) #is_valid判斷我們字段是否有錯(cuò)執(zhí)行我們原有邏輯,驗(yàn)證失敗跳回login頁面 if login_form.is_valid(): # 取不到時(shí)為空,username,password為前端頁面name值 user_name = request.POST.get("username", "") pass_word = request.POST.get("password", "") # 成功返回user對象,失敗返回null user = authenticate(username=user_name, password=pass_word) # 如果不是null說明驗(yàn)證成功 if user is not None: # login_in 兩參數(shù):request, user # 實(shí)際是對request寫了一部分東西進(jìn)去,然后在render的時(shí)候: # request是要render回去的。這些信息也就隨著返回瀏覽器。完成登錄 login(request, user) # 跳轉(zhuǎn)到首頁 user request會(huì)被帶回到首頁 return render(request, "index.html") # 驗(yàn)證不成功跳回登錄頁面 # 沒有成功說明里面的值是None,并再次跳轉(zhuǎn)回主頁面 else: return render(request, "login.html", {"msg": "用戶名或密碼錯(cuò)誤! "})完善錯(cuò)誤提示
比如:既然表單都驗(yàn)證失敗了,就不用顯示密碼出錯(cuò)了
mark # 僅當(dāng)用戶真的密碼出錯(cuò)時(shí)else:return render(request, "login.html",{"msg":"用戶名或密碼錯(cuò)誤!"}) # 驗(yàn)證不成功跳回登錄頁面 # 沒有成功說明里面的值是None,并再次跳轉(zhuǎn)回主頁面 else: return render( request, "login.html", { "login_form": login_form })forms中的名稱username和password必須和html中的一致。畢竟他是使用的request.POST
而request是從前面?zhèn)鬟^來的。
實(shí)例化LoginView時(shí)已經(jīng)對于我們的字段進(jìn)行了驗(yàn)證。
打上斷點(diǎn):
markdebug后f6運(yùn)行到
mark此時(shí)可以看到errors(ErrorDict)中的錯(cuò)誤
mark將form傳回前端:
mark前端中取值:
mark給這個(gè)class加上errorput會(huì)顯示紅色外框。
mark注意:寫在class里面
將forms錯(cuò)誤信息顯示出來
<div class="error btns login-form-tips" id="jsLoginTips"> {% for key, error in login_form.errors.items %} {{ error }} {% endfor %} {{ msg }}</div> mark- 寫了一個(gè)類繼承Django的view,然后寫了get post方法(get/post的if是Django替我們完成的)
- 在url中調(diào)用Loginview的as_view方法需要加上括號(hào),進(jìn)行調(diào)用。
- Django的form進(jìn)行表單驗(yàn)證并把error值傳到前臺(tái)。
- is_valid方法,驗(yàn)證表單
本小節(jié)完畢對應(yīng)commit:
6-4 & 5 登錄換用類繼承view實(shí)現(xiàn),使用Django form進(jìn)行表單驗(yàn)證并把錯(cuò)誤信息提示到前臺(tái)。
6-6 session和cookie自動(dòng)登錄機(jī)制
我們本節(jié)來講session和cookie
User1如何實(shí)現(xiàn)登錄的。
cookie的存儲(chǔ)
cookie是瀏覽器支持的一種本地存儲(chǔ)方式。以dict,鍵值對方式存儲(chǔ)。
{"sessionkey": "123"}瀏覽器會(huì)自動(dòng)對于它進(jìn)行解析。
http請求是一種無狀態(tài)的請求
用戶向服務(wù)器發(fā)起的兩次請求之間是沒有狀態(tài)的。也就是服務(wù)器并不知道這是同一個(gè)用戶發(fā)的。
做到記住用戶:
瀏覽器a在向服務(wù)器發(fā)起請求,服務(wù)器會(huì)自動(dòng)給瀏覽器a回復(fù)一個(gè)id,瀏覽器a把id放到cookie當(dāng)中,在下一次請求時(shí)帶上這個(gè)cookie里的id值向?yàn)g覽器請求,服務(wù)器就知道你是哪個(gè)瀏覽器發(fā)過來的了。
有狀態(tài)請求(cookie)
mark服務(wù)器a發(fā)回來的id會(huì)放到服務(wù)器a的域之下。不能跨域訪問cookie。
使用瀏覽器隨便打開一個(gè)網(wǎng)頁,然后f12打開。
比如我使用的Chrome瀏覽器
mark會(huì)找到存儲(chǔ)在瀏覽器本地的cookie值
mark點(diǎn)擊clear all清空所有的cookie f5刷新頁面,會(huì)發(fā)現(xiàn)又把這些cookie值進(jìn)來。
如果將用戶名和密碼直接保存在cookie,可以實(shí)現(xiàn)最垃圾最簡略版本的自動(dòng)登錄。
解決cookie放在本地不安全的問題(session)
用戶在第一次請求后,瀏覽器回復(fù)的id既可以是用戶的user id。
也可以一段任意的字符串,我們把它叫做session id
根據(jù)用戶名和密碼,服務(wù)器會(huì)采用自己的規(guī)則生成session id。這個(gè)session id保存在本地cookie。瀏覽器請求服務(wù)器會(huì)攜帶。
- 輸入用戶名 & 密碼
- 調(diào)用 login(), 后端程序會(huì)根據(jù)用戶名密碼生成session id。保存在數(shù)據(jù)庫中。
- 用戶登錄之后,需要通過這個(gè)session id取出這些基本信息。
Django的默認(rèn)表中的session表就記錄了用戶登錄時(shí),后端我們Django為用戶生成的sessionid。
mark可以看到session key value 和過期時(shí)間。
我們可以清空這張表的數(shù)據(jù)。運(yùn)行項(xiàng)目進(jìn)行登錄。
mark可以看到我們剛剛生成的session id。
此時(shí)通過f12查看瀏覽器在本地存儲(chǔ)的session id。可以看到如下圖和我們數(shù)據(jù)庫中的一致。
marksession_key 發(fā)到瀏覽器叫做session id
通過session id 用戶訪問任何一個(gè)頁面都會(huì)攜帶,服務(wù)器就會(huì)認(rèn)識(shí)。
Setting.py中,
mark這個(gè)app會(huì)攔截我們每次的request請求,在request中找到session id,然后去數(shù)據(jù)表中進(jìn)行查詢。
然后通過session key 去找到session data。此時(shí)直接為我們?nèi)〕隽藆ser。
在服務(wù)器返回瀏覽器的response中也會(huì)直接加上session id
cookie是瀏覽器本地存儲(chǔ)機(jī)制,存在域名之下,存儲(chǔ)不安全。
服務(wù)器在返回id時(shí)通過規(guī)則生成一串字符,并設(shè)置了過期時(shí)間。存儲(chǔ)在服務(wù)器端(數(shù)據(jù)庫)
文章: http://projectsedu.com/2016/10/17/django%E4%BB%8E%E8%AF%B7%E6%B1%82%E5%88%B0%E8%BF%94%E5%9B%9E%E9%83%BD%E7%BB%8F%E5%8E%86%E4%BA%86%E4%BB%80%E4%B9%88/
6-7 用戶注冊
拷貝注冊頁面進(jìn)入template目錄
書寫我們對應(yīng)要處理的view(RegisterView)
users/views.py
# 注冊功能的view class RegisterView(View): # get方法直接返回頁面 def get(self, request): return render(request, "register.html", {})配置對應(yīng)的url
Django1.9.8 url配置如下:
from users.views import RegisterView# 注冊urlurl("^register/", RegisterView.as_view(), name="register"),Django2.0.1 url配置如下
from users.views import RegisterView# 注冊urlpath("register/", RegisterView.as_view(), name = "register" )修改index頁面中注冊url
mark此時(shí)訪問首頁發(fā)現(xiàn)可以成功跳轉(zhuǎn)到注冊頁面
修改靜態(tài)文件中static目錄引用
關(guān)鍵步驟load staticfile
mark然后修改路徑為一個(gè)相對于static的相對路徑
mark他會(huì)自動(dòng)根據(jù)setting中配置,為我們加上前綴
mark如果我們把目錄在setting中改到mystatic。url中會(huì)自動(dòng)添加指定的前綴
可以看到可以訪問成功。
mark將目前的三個(gè)html中的靜態(tài)文件全部修改目錄
枯燥但是要有耐心。
這時(shí)候訪問三個(gè)頁面,查看樣式是否完好。
驗(yàn)證碼庫實(shí)現(xiàn)驗(yàn)證碼
https://github.com/mbi/django-simple-captcha
安裝配置
workon mxonline3 pip install django-simple-captcha workon mxonline2 pip install django-simple-captcha==0.4.6-
Add captcha to the INSTALLED_APPS in your settings.py
-
Add an entry to your urls.py:
django1.9.8如下:
from django.conf.urls import url, include urlpatterns += [url(r'^captcha/', include('captcha.urls')), ]django2.0.1如下;
# 驗(yàn)證碼urlpath("captcha/", include('captcha.urls')) makemigrations migrate mark進(jìn)入數(shù)據(jù)庫查看生成的表
mark mark將驗(yàn)證碼展示到頁面
users/forms.py:
定義我們的register form:
# 引入驗(yàn)證碼field from captcha.fields import CaptchaField# 驗(yàn)證碼form & 注冊表單form class RegisterForm(forms.Form): # 此處email與前端name需保持一致。 email = forms.EmailField(required=True) # 密碼不能小于5位 password = forms.CharField(required=True, min_length=5) # 應(yīng)用驗(yàn)證碼 captcha = CaptchaField()users/views.py
在我們的registerform中實(shí)例化并傳送到前端:
# form表單驗(yàn)證 & 驗(yàn)證碼 from .forms import LoginForm, RegisterForm# 注冊功能的view class RegisterView(View): # get方法直接返回頁面 def get(self, request): # 添加驗(yàn)證碼 register_form = RegisterForm() return render(request, "register.html", {'register_form':register_form})前端獲取驗(yàn)證碼值
mark mark找到上圖驗(yàn)證碼部分。修改為下圖
markForms中的field會(huì)生成不同的框。
mark我們只有l(wèi)abel但是前端可以查看到input框等,也就是Registerform會(huì)為我們生成輸入框+驗(yàn)證碼。
隱藏的字符串的框會(huì)被帶到后臺(tái),由Django為我們進(jìn)行驗(yàn)證。驗(yàn)證該驗(yàn)證碼是否保存過。
mark可以看得我們數(shù)據(jù)庫中將這個(gè)hashkey進(jìn)行了保存。這個(gè)key與驗(yàn)證碼內(nèi)容對應(yīng)。
后臺(tái)會(huì)把驗(yàn)證碼值 和 hashkey進(jìn)行聯(lián)合查詢。
編寫register view的后臺(tái)邏輯(RegisterView)
users/views.py的RegisterView中添加post方法:
def post(self, request): # 實(shí)例化form register_form = RegisterForm(request.POST) if register_form.is_valid(): pass mark mark修改form表單提交方式與提交到哪個(gè)url
mark前端的form提交加上對應(yīng)的crsf token
刷新驗(yàn)證碼是前端幫我們完成的:
//刷新驗(yàn)證碼 function refresh_captcha(event){ $.get("/captcha/refresh/?"+Math.random(), function(result){ $('#'+event.data.form_id+' .captcha').attr("src",result.image_url); $('#id_captcha_0').attr("value",result.key); }); return false; }獲取前端頁面值并封裝成一個(gè)user_profile對象,保存到數(shù)據(jù)庫。
from django.contrib.auth.hashers import make_passwordif register_form.is_valid():user_name = request.POST.get("email", "") pass_word = request.POST.get("password", "") # 實(shí)例化一個(gè)user_profile對象,將前臺(tái)值存入 user_profile = UserProfile() user_profile.username = user_name user_profile.email = user_name # 加密password進(jìn)行保存 user_profile.password = make_password(pass_word) user_profile.save() pass發(fā)送郵件實(shí)現(xiàn)
setting中配置;
# 發(fā)送郵件的setting設(shè)置EMAIL_HOST = "smtp.qq.com" EMAIL_PORT = 25 EMAIL_HOST_USER = "mxonline.mtianyan.cn" EMAIL_HOST_PASSWORD = " " EMAIL_USE_TLS= True EMAIL_FROM = "mxonline.mtianyan.cn"新建package后新建文件。
apps:utils/email_send.py:
# encoding: utf-8 from random import Random__author__ = 'mtianyan' __date__ = '2018/1/10 0010 20:47' from users.models import EmailVerifyRecord # 導(dǎo)入Django自帶的郵件模塊 from django.core.mail import send_mail # 導(dǎo)入setting中發(fā)送郵件的配置 from Mxonline2.settings import EMAIL_FROM # 生成隨機(jī)字符串 def random_str(random_length=8): str = '' # 生成字符串的可選字符串 chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' length = len(chars) - 1 random = Random() for i in range(random_length): str += chars[random.randint(0, length)] return str # 發(fā)送注冊郵件 def send_register_eamil(email, send_type="register"): # 發(fā)送之前先保存到數(shù)據(jù)庫,到時(shí)候查詢鏈接是否存在 # 實(shí)例化一個(gè)EmailVerifyRecord對象 email_record = EmailVerifyRecord() # 生成隨機(jī)的code放入鏈接 code = random_str(16) email_record.code = code email_record.email = email email_record.send_type = send_type email_record.save() # 定義郵件內(nèi)容: email_title = "" email_body = "" if send_type == "register": email_title = "mtianyan慕課小站 注冊激活鏈接" email_body = "請點(diǎn)擊下面的鏈接激活你的賬號(hào): http://127.0.0.1:8000/active/{0}".format(code) # 使用Django內(nèi)置函數(shù)完成郵件發(fā)送。四個(gè)參數(shù):主題,郵件內(nèi)容,從哪里發(fā),接受者list send_status = send_mail(email_title, email_body, EMAIL_FROM, [email]) # 如果發(fā)送成功 if send_status: pass mark上圖為qq郵箱開啟smtp服務(wù)
點(diǎn)擊生成授權(quán)碼
markdef post中加上發(fā)送郵件
users/views.py:
# 發(fā)送郵件 from utils.email_send import send_register_eamil# 發(fā)送注冊激活郵件 send_register_eamil(user_name, "register")點(diǎn)擊注冊提交,因?yàn)槲覀儧]有return。一直在轉(zhuǎn)圈圈。
但是數(shù)據(jù)庫中已經(jīng)添加了字段。
mark可以看到我們的郵件已經(jīng)被發(fā)送到郵箱中。
如果注冊成功返回login頁面:不成功,返回register頁面并報(bào)錯(cuò)。
完善錯(cuò)誤提示。
找貓畫虎:將login中的錯(cuò)誤提示搬運(yùn)到register中來。
- register_form的報(bào)錯(cuò)信息。
- 郵箱 & 密碼 form驗(yàn)證
完善用戶值回填邏輯
mark如果傳回的有值則,顯示傳回來值。
密碼也做同樣操作
修改默認(rèn)的激活狀態(tài)為false
post方法中
# 默認(rèn)激活狀態(tài)為falseuser_profile.is_active = False書寫處理激活的view。
# 激活用戶的view class ActiveUserView(View): def get(self, request, active_code): # 查詢郵箱驗(yàn)證記錄是否存在 all_record = EmailVerifyRecord.objects.filter(code = active_code) # 激活form負(fù)責(zé)給激活跳轉(zhuǎn)進(jìn)來的人加驗(yàn)證碼 active_form = ActiveForm(request.GET) # 如果不為空也就是有用戶 if all_record: for record in all_record: # 獲取到對應(yīng)的郵箱 email = record.email # 查找到郵箱對應(yīng)的user user = UserProfile.objects.get(email=email) user.is_active = True user.save() # 激活成功跳轉(zhuǎn)到登錄頁面 return render(request, "login.html", ) # 自己瞎輸?shù)尿?yàn)證碼 else: return render(request, "register.html", {"msg": "您的激活鏈接無效","active_form": active_form})配置用戶激活的url并通過url提取到變量:
django1.9.8:
# 激活用戶urlurl(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(), name= "user_active")django2.0.1:
# 激活用戶urlre_path('active/(?P<active_code>.*)/', ActiveUserView.as_view(), name= "user_active")這里通過?p將后面.*代表全部提取的正則,符合的內(nèi)容傳入?yún)?shù)active_code中/$代表以/$為結(jié)尾
mark其他細(xì)節(jié)根據(jù)自己需要進(jìn)行優(yōu)化。
注冊功能制作完畢。對應(yīng)commit:
注冊功能實(shí)現(xiàn)完畢,流程:注冊,發(fā)郵件,激活,登錄。對應(yīng)6-6,7,8,9,10
原文學(xué)習(xí)來自簡書,作者:天涯明月笙
鏈接:https://www.jianshu.com/p/9c621518d991
轉(zhuǎn)載于:https://www.cnblogs.com/xinjie57/p/9232627.html
總結(jié)
以上是生活随笔為你收集整理的django+xadmin在线教育平台(十二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: openstack及组件简要介绍
- 下一篇: ACM-ICPC 2018 徐州赛区网络