一、全自動(推薦使用的**)css
優點:第三張能夠任意的擴展字段html
缺點:ORM查詢不方便,若是後續字段增長更改時不便添加修改前端
manyToManyField建立的第三張表屬於虛擬的,後綴會自動添加有_id的外鍵字段python
建立的方式:jquery
二、純手動(不推薦使用)正則表達式
須要手動創第三方表數據庫
優點:第三張表能夠任意的擴展字段django
缺點:ORM查詢不便後端
三、半自動(推薦使用***)瀏覽器
優點:結合了全自動和半自動的兩個優勢,把建表的關係直接在一張表上表示出來
這樣建立的表,在多對多時不支持ORM的操做有如下幾種:
""" 多對多字段的 add set remove clear不支持 """
一、校驗功能:
二、標籤的渲染功能
三、渲染錯誤的信息
四、局部鉤子,再次檢驗
簡單的註冊功能的版本:
實現簡單的校驗功能。設置條件,在註冊的時候進行有效的檢驗
from django.shortcuts import render,reverse,redirect,HttpResponse from django.core.exceptions import ValidationError # Create your views here. def login(request): errors = {'username':'','password':''} if request.method=='POST': username = request.POST.get('username') password = request.POST.get('password') if '黃賭毒' in username: errors['username'] = '屬於違法,警戒' if len(password)<6: errors['password'] = '密碼太短,不安全' return render(request,'login.html',locals())
login.html:
在提交的按鈕,輸入框中綁定提示的錯誤信息,校驗的條件在後端進行
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <form action="" method="post"> <p>username: <input type="text" name="username"> <span style="color: red">{{ errors.username }}</span> </p> <p>password: <input type="password" name="password"> <span style="color: red">{{ errors.password }}</span> </p> <input type="submit"> </form> </body> </html>
總結:forms組件可以直接完成如下的三部分
1.渲染前端頁面
2.校驗數據是否合法
3.展現錯誤信息
基本的校驗規則:
先寫好一個繼承了forms.Form的類
from django import forms class LoginForm(forms.Form): username = forms.CharField(max_length=8,min_length=3) # 用戶名最長八位最短三位 password = forms.CharField(max_length=8,min_length=5) # 密碼最長八位最短五位 email = forms.EmailField() # email必須是郵箱格式
forms的校驗規則:根據字段一個個進行比對,符合條件和錯誤的分別的保存起來
1.將須要校驗的數據,以字典的方式傳遞給自定義的類,實例化產生對象
form_obj = views.LoginForm({'username':'jason','password':'123','email':'123'})
2.如何查看數據是否所有合法
form_obj.is_valid() # 只有全部的數據都符合要求 纔會是True
False
3.如何查看錯誤緣由
form_obj.errors
{
'password': ['Ensure this value has at least 5 characters (it has 3).'],
'email': ['Enter a valid email address.']
}
4.如何查看經過校驗的數據
form_obj.cleaned_data
{'username': 'jason'}
直接在pycharm終端輸入命令進行校驗:
注意事項:
1.自定義類中全部的字段默認都是必需要傳值的
2.能夠額外傳入類中沒有定義的字段名 forms組件不會去校驗 也就意味着多傳一點關係沒有
書寫from表單的三種方法:
前端簡單的from表單以下,原始的
在Django中利用forms組件書寫的from表單又如下三種方法:
先在後端開啓路由向前端傳遞數據信息,views.py
def register(request): # 先生成一個空白的自定義類對象 form_obj = LoginForm() # 將該對象傳遞給前端 return render(requesr,'register.html')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <form action="" method='post' novalidate> <p>第一種渲染頁面的方式(封裝程度過高 通常只用於本地測試 一般不適用)</p> {{ form_obj.as_p }} {{ form_obj.as_ul }} {{ form_obj.as_table }} <p>第二種渲染頁面的方式(可擴展性較高 書寫麻煩)</p> <p>{{ form_obj.username.label }}{{ form_obj.username }}</p> <p>{{ form_obj.password.label }}{{ form_obj.password }}</p> <p>{{ form_obj.email.label }}{{ form_obj.email }}</p> <p>第三種渲染頁面的方式(推薦)</p> {% for foo in form_obj %} <p>{{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form> </body> </html>
moldes.py,建表的時候指定校驗的方式,和條件
from django import forms from django.core.validators import RegexValidator from django.forms import widgets class LoginForm(forms.Form): username = forms.CharField(max_length=8,min_length=3,label='用戶名',initial='tankdsb', error_messages={ 'max_length':'用戶名最大八位', 'min_length':'用戶名最小三位', 'required':'用戶名不能爲空' },widget=widgets.TextInput() ) # 用戶名最長八位最短三位 password = forms.CharField(max_length=8,min_length=5,label='密碼',error_messages={ 'max_length':'密碼最大八位', 'min_length':'密碼最小五位', 'required':'密碼不能爲空' }, widget=widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'}) ) # 密碼最長八位最短五位 confirm_password = forms.CharField(max_length=8, min_length=5, label='確認密碼', error_messages={ 'max_length': '確認密碼最大八位', 'min_length': '確認密碼最小五位', 'required': '確認密碼不能爲空' }, required=False, validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')]) # 密碼最長八位最短五位 email = forms.EmailField(label='郵箱',error_messages={ 'required':'郵箱不能爲空', 'invalid':'郵箱格式不正確' }) # email必須是郵箱格式
views.py
def reg(request): # 1 現生成一個空的自定義類的對象 form_obj = LoginForm() # 2 將該對象傳遞給前端頁面 if request.method == 'POST': # 3 獲取前端post請求提交過來的數據 # print(request.POST) # 因爲request.POST其實也是一個字典,全部能夠直接傳給LoginForm form_obj = LoginForm(request.POST) # 4 校驗數據 讓forms組件幫你去校驗 if form_obj.is_valid(): # 5 若是數據所有經過 應該寫入數據庫 pass # 6 若是不經過 一個像前端展現錯誤信息 return render(request,'reg.html',locals())
前端html
注意事項:
1.forms組件在幫你渲染頁面的時候,只會渲染獲取用戶輸入的標籤 ,提交按鈕須要你手動添加
2.input框的label註釋 ,不指定的狀況下,默認用的類中字段的首字母大寫
告訴前端取消掉自帶的校驗方式,form標籤指定novalidate屬性便可,讓後端來書寫
掌握的自定義字段,用正則表達式書寫校驗規則,手機號的驗證:
RegexValidator驗證器
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')], )
import re from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.exceptions import ValidationError # 自定義驗證規則 def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手機號碼格式錯誤') class PublishForm(Form): title = fields.CharField(max_length=20, min_length=5, error_messages={'required': '標題不能爲空', 'min_length': '標題最少爲5個字符', 'max_length': '標題最多爲20個字符'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': '標題5-20個字符'})) # 使用自定義驗證規則 phone = fields.CharField(validators=[mobile_validate, ], error_messages={'required': '手機不能爲空'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手機號碼'})) email = fields.EmailField(required=False, error_messages={'required': u'郵箱不能爲空','invalid': u'郵箱格式錯誤'}, widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'郵箱'}))
就是forms組件暴漏給用戶的,能夠自定義額外的檢驗規則;必須寫在Form類裏面
咱們在Fom類中定義 clean_字段名() 方法,就可以實現對特定字段進行校驗。
局部鉤子(針對某一個字段作額外的校驗) 校驗用戶名中不能包含666 一旦包含 提示
def clean_username(self): username = self.cleand_data.get('username') if '666' in username: self.add_error('username','密碼有危險提示,請重新修改哦') return username
全局鉤子(針對多個字段作額外的校驗) 校驗用戶兩次密碼是否一致
有password和confirm_password兩個字段:
# 全局鉤子(針對多個字段作額外的校驗) 校驗用戶兩次密碼是否一致 def clean(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if not password == confirm_password: self.add_error('confirm_password','兩次密碼不一致') return self.cleaned_data
""" required 是否必填 label 註釋信息 error_messages 報錯信息 initial 默認值 widget 控制標籤屬性和樣式 widget = widgets.PasswordInput() 控制標籤屬性 widget = widgets.PasswordInput(attrs={'class': 'form-control c1 c2', 'username': 'jason'}) """
多個類就空格格開書寫:c2 c2
# 單選的radio框 gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性別", initial=3, widget=forms.widgets.RadioSelect() ) # 單選select hobby = forms.ChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=3, widget=forms.widgets.Select() ) # 多選的select框 hobby1 = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) # 單選的checkbox keep = forms.ChoiceField( label="是否記住密碼", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多選的checkbox hobby2 = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) phone = forms.CharField( validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')], )
具體的展現:
經過源碼解析,看Django內部的forms組件是如何是實現校驗的原理的;大體瞭解
因爲Http協議的無狀態的特色,也就是說即便第一次和服務器鏈接後而且登陸成功後,
第二次請求服務器依然不能知道當前請求是哪一個用戶,cookies的出現就是爲了解決這一現象。
cooki就是保存在客戶端瀏覽器上的鍵值對:
工做原理:當用戶登陸成功後,瀏覽器上會保存一些信息,下次再來訪問的時候,就會攜帶着這些信息去訪問服務端,服務端經過這些信息來識別出你的身份
session和cookie的做用有點相似,都是爲了存儲用戶相關的信息。不一樣的是,cookie是存儲在本地瀏覽器,而session存儲在服務器。存儲在服務器的數據會更加的安全,
不容易被竊取。但存儲在服務器也有必定的弊端,就是會佔用服務器的資源,但如今服務器已經發展至今,一些session信息仍是綽綽有餘的。
session就是保存在服務器上的鍵值對
session雖然是保存在服務器上的鍵值對
可是它是依賴於cookie工做的
前提分析:Django返回給客戶端瀏覽器的都必須是HttpResponse對象;
三板斧:
return HttpResonse(): 返回字符串
return render() 返回html頁面
return redirect() 重定向到...
經過對cookie的操做,讓服務器如何存儲數據信息,保存用戶的登陸狀態,簡單的寫一個都登陸註冊的例子:
首先先掌握如何操做cookie:
request.COOKIE.get()
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
參數:
rep = HttpResponse(...) rep = render(request, ...) rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)
設置超時時間
max_age=None, 超時時間
expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
刪除Cookie
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 刪除用戶瀏覽器上以前設置的usercookie值 return rep
views,py
def lg(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'jack' and password == '123': # 先獲取url中get請求攜帶的參數 old_url = request.GET.get('next') # 判斷用戶是直接訪問的登錄頁面 仍是從別的頁面的調過來 if old_url: obj = redirect(old_url) else: # 若是用戶直接訪問的登錄頁面 那麼登錄完成以後 直接跳到網站的首頁 obj = redirect('/home/') obj.set_cookie('name','jason',max_age=30) # 瀏覽器上就會保存鍵值對name:jack return obj return render(request,'login.html')
hime返回主頁面的功能
def home(request): if request.COOKIES,get('name'): return HttpResonse('home主頁面,只有登陸了才能夠進來!') return redirect('login') return HttPResonse('home頁面,只有登陸了才能看!')
若是頁面過多,訪問時都須要設置先登陸,這時候就想到了設置一個登陸裝飾器,在全局哪裏須要就往那裏加。
from functools import wraps def login_auth(func): @wraps(func) def inner(request,*args,**kwargs): # 從request中獲取cookie # print(request.path) # print(request.get_full_path()) target_url = request.get_full_path()# 獲取url的後綴/?next=%s if request.COOKIES.get('name'): res = func(request,*args,**kwargs) return res else: return redirect('/login/?next=%s'%target_url) return inner
利用裝飾器,開設其餘跳轉頁面功能
@login_auth def index(request): return HttpResponse("index頁面 只有登陸了才能訪問") @login_auth def xxx(request): return HttpResponse('xxx頁面 登錄以後才能看')
註銷功能刪除cookie保存的數據,下次登陸就不會再保存原有的數據進行校驗,得從新登陸
def logout(request):
obj = redirect('/lg/')
obj.delete_cookie('name')
return obj
login.html前端的簡單書寫from表單,按鈕實現登陸接口
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <form action="" method="post"> <p>username: <input type="text" name="username"> <span style="color: red">{{ errors.username }}</span> </p> <p>password: <input type="password" name="password"> <span style="color: red">{{ errors.password }}</span> </p> <input type="submit"> </form> </body> </html
分析以上頁面之間跳轉的實現原理:基於cookie的原理,用獲取和設置cookie,保存cookie所保存的用戶數據信息,
下次再次訪問的時候攜帶上cookie在客戶端保存的用戶信息進行校驗比對,是否屬於登陸的狀態,登陸成功或自動保存。
獲取、設置、刪除Session中數據
request.session['k1'] request.session.get('k1',None) 取值通常應set的方式 request.session['k1'] = 123 賦值 request.session.setdefault('k1',123) # 存在則不設置 del request.session['k1']
session保存在服務端的,數據保存在Django默認自帶的數據庫表,當執行數據遷移命令的時候都會自帶一個django_session表;
設置session request.session['name'] = 'jack' """ 上面這一句話發生了三件事 1.django 內部自動生成一個隨機字符串 2.將隨機字符串和你要保存的數據 寫入django_session表中(先在內存中生成一個緩存記錄,等到通過中間件的時候纔會執行) 3.將產生的隨機字符串發送給瀏覽器寫入cookie sessionid:隨機字符串 """ 獲取session request.session.get('name') """ 上面這一句話發生了三件事 1.django內部會自動從請求信息中獲取到隨機字符串 2.拿着隨機字符串去django_session表中比對 3.一旦對應上了就將對應的數據解析出來放到request.session中 """
方法的使用
def set_session(request): # request.session['name'] = 'jason' # request.session['name1'] = 'jason1' request.session['xxx'] = 'xxx' request.session.set_expiry(30) return HttpResponse('set_session') def get_session(request): # print(request.session.get('name')) # print(request.session.get('name3')) print(request.session.get('xxx')) return HttpResponse('set_session') def delete_session(request): # request.session.delete('xxx') request.session.flush() return HttpResponse('delete_session')
解析:
刪除當前會話的全部Session數據 request.session.delete() # 刪除的是瀏覽器的sessionid信息 # 刪除當前的會話數據並刪除會話的Cookie。 request.session.flush() # 將瀏覽器和服務端所有刪除 這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問 例如,django.contrib.auth.logout() 函數中就會調用它。 # 設置會話Session和Cookie的超時時間 request.session.set_expiry(value) *若是value是個整數,session會在些秒數後失效。 *若是value是個datatime或timedelta,session就會在這個時間後失效。 *若是value是0, 用戶關閉瀏覽器session就會失效。 *若是value是None, session會依賴全局session失效策略。
總結:你在後期能夠將一些數據保存到session表中,保存的數據 能夠在後端任意位置獲取到