一。多對多建表關係之手動添加。html
1。全自動前端
像以前講過的同樣,咱們能夠經過manytomanyField的字段來創建多對多關係:python
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
優勢:不須要你手動建立第三張表,並且可使用外鍵查詢表。git
缺點。因爲第三張表不是本身手動建立的,因此第三張表的字段是固定的沒法增長字段。數據庫
2.純手動。django
再多對都的建立中,也能夠手動建立第三張表,而後使用外鍵鏈接到兩個表之間。後端
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') create_time = models.DateField(auto_now_add=True)
優勢:能夠任意添加擴展的字段。瀏覽器
缺點:不能使用orm中的外鍵查詢。緩存
3.半自動服務器
在建立第三張表的狀況下,建立1,2張表的manytomany到該表中
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) authors = models.ManyToManyField (to='Author',through='Book2Author',through_fields=('book','author')) # through 告訴django orm 書籍表和做者表的多對多關係是經過Book2Author來記錄的 # through_fields 告訴django orm記錄關係時用過Book2Author表中的book字段
和author字段來記錄的 class Author(models.Model): name = models.CharField(max_length=32) # books = models.ManyToManyField
(to='Book', through='Book2Author', through_fields=('author', 'book')) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') create_time = models.DateField(auto_now_add=True)
manytomany字段中的各個參數的意義是:
to:關聯的那張表。
through:綁定的第三張表。
throuth_fields:記錄關係時用到的字段,前面時本表查詢他表時通過的字段
這種方法定義的表不適合使用如下查詢或設置函數:
add set remove chear。
二。forms組件。
在前端輸入用戶名和密碼時,會有提示你的密碼過於短等,這些都是forms的做用,用來限制你輸入的內容並報錯,咱們能夠手動實現一個簡單的版本:
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) < 3: errors['password'] = '過短了' return render(request,'login.html',locals())
<form action="" method="post"> <p>username:<input type="text" name="username"><span>{{ errors.username }}</span></p> <p>password:<input type="password" name="password"><span>{{ errors.password }}</span></p> <p><input type="submit" value="提交"></p> </form>
剛剛的代碼種會出現如下步驟:
1.前端頁面搭建。
2。將數據傳輸到後端作校驗。
3.展現錯誤信息。
而使用forms組件會出現如下步驟:
1.渲染前端頁面
2.校驗數據是否合法
3.展現錯誤信息
1.基本的forms用法:
這個組件須要導入模塊,語法和orm差很少:
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必須是郵箱格式
在測試這些代碼時,能夠在python中的終端中測試,可是須要先導入相關模塊,本地路勁是當前項目的路徑。
1.若是須要校驗數據,就須要以字典 的方式傳遞給自定義的類,它會產生對象,經過該對象的一些方法就能夠實現檢測:
form_obj = views.LoginForm({'username':'jason','password':'123','email':'123'})
2.使用is_valid()方法就能夠判斷該字典中的數據是否都符合要求。
form_obj.is_valid() # 只有全部的數據都符合要求 纔會是True False
3.查看錯誤緣由,當使用errors時,會返回一個字典加列表的數據,其中就有該數據的錯誤緣由:
form_obj.errors {'password': ['Ensure this value has at least 5 characters (it has 3).'], 'email': ['Enter a valid email address.'] }
4.如何查看經過校驗的數據:cleaned_data方法
form_obj.cleaned_data {'username': 'jason'}
在使用forms字段時:
1.當輸入的字典中有其餘自定義類中的數據時,不會出錯,forms也不會處理。
2.當輸入的字典中沒有自定義的類中的一些字段時,會出錯,is_valid()會時false。
3。具體流程就是將字典中的字段和類中的字段一一匹配,再比較限定條件。
2.渲染頁面的方法:
再頁面中,能夠直接將該類生成的對象返回給前端,前端段能夠直接進行渲染。
1.:過分封裝的渲染方法:
第一種渲染頁面的方式(封裝程度過高 通常只用於本地測試 一般不適用):
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
as_p就是將結果變成p格式的
as_ul就是變成列表形式的。
as_table就是普通格式的。
2.擴展性較高的方法,
這種方法書寫麻煩,每一個對象中點出其中的label和其中的名字。
<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>
3.可批量的渲染:
將該對象for循環能夠得出其中的全部字段信息。
{% for foo in form_obj %} <p>{{ foo.label }}{{ foo }}
<span>{{ foo.errors.0 }}</span>
</p>
{% endfor %}
3.錯誤信息展現。
使用foo.errors能夠將錯誤信息以列表的形式展出,可是會改變原來的板塊,因此須要再後面.0取出其中的原本的錯誤信息。
提交按鈕不會幫你添加,須要手動添加。
當form中的label沒有指定的時候,就是該字段名的首字母大寫。label表示的是這個字段表明的內容名字
在前端,這些限制已經有了做用,可是這些限制在爬蟲的對抗上沒有什麼用,因此須要在前端和 後端都加上限制。爲了取消前端的限制,測試後端限制,須要給表單加上關鍵字novalidate
在普通字段中,須要顯示中文的錯誤提示,須要對其error_messages進行設置。其中的值表明的是:
1.max_length,最大的。。這些都是在定義字段是本身定義的。
2.required表明爲空是報的錯。
3.invalid表明郵箱的格式是否正確。
例子:
password = forms.CharField(max_length=8,min_length=5,label='密碼',error_messages={ 'max_length':'密碼最大八位', 'min_length':'密碼最小五位', 'required':'密碼不能爲空' },)
在字段中也有可使用正則匹配的限制條件,關鍵字是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'郵箱'}))
三。鉤子函數。
當你的原本字段中的函數限制校驗經過後,還須要進行校驗是,就須要經過鉤子函數。
改函數須要返回對應的字段
鉤子函數的定義是在自定義類中定義,類中會將你定義的字段錢加clean_封裝好,定義的時候有兩種報錯方式:
def clean_username(self): username = self.cleaned_data.get('username') if '666' in username: # raise ValidationError('奧術大師就卡的凱撒就確定會') self.add_error('username','光喊666是不行的 你得本身上') return username
一種是raise,一種是add_error。
還有一種是全局鉤子,能夠跨字段進行校驗:
返回值是數據字典。
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關鍵字,還能夠用來操做class,也就是添加類:
widget=widgets.PasswordInput(
attrs={'class':'form-control c1 c2','username':'jason'})
其餘類型字段:
1.radioSelect
gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性別", initial=3, widget=forms.widgets.RadioSelect() )
2.單選Select
hobby = forms.ChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=3, widget=forms.widgets.Select() )
3.多選Select
hobby1 = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.SelectMultiple() )
4.單選checkbox
keep = forms.ChoiceField( label="是否記住密碼", initial="checked", widget=forms.widgets.CheckboxInput() )
5.多選checkbox
hobby2 = forms.MultipleChoiceField( choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),), label="愛好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
以上的代碼中initial是其默認選擇項,widget表明其中的真實數據。
is_valid原理:
首先先獲取全部的字段的信息,放入全局的cleaned_data中,再調用全局的校驗方法,進行校驗,有錯就會報錯。
五。cookies和session
因爲http協議是無狀態的,法記錄用戶狀態 。
1.cookies
cookie就是保存在客戶端瀏覽器上的鍵值對。
工做原理:當你登錄成功以後 瀏覽器上會保存一些信息。下次再訪問的時候 就會帶着這些信息去訪問服務端 服務端經過這些信息來識別出你的身份。
cookie雖然是寫在客戶端瀏覽器上的 可是是服務端設置的。瀏覽器能夠選擇不服從命令 禁止寫cookie。
2.session
session就是保存在服務器上的鍵值對。
session雖然是保存在服務器上的鍵值對。可是它是依賴於cookie工做的。
服務端返回給瀏覽器一個隨機的字符串。瀏覽器以鍵值對的形式保存。
sessionid就是一個隨機字符串。瀏覽器在訪問服務端的時候 就會將隨機字符串攜帶上。後端獲取隨機串與後端的記錄的作比對。如:
隨機字符串1:數據1。
隨機字符串2:數據2
查看Cookie
咱們使用Chrome瀏覽器,打開開發者工具。
六。cookies的獲取和刪除。
cookies的獲取能夠從COOKIES中獲取。
設置cookie利用的就是HttpResponse對象
obj1.set_cookie('k1','v1')
獲取cookie
request.COOKIE.get()
request.COOKIES['key'] request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
而再設置cookies中是以鍵值對的方式設置。設置的對象就是HttpResponse對象,最後再返回這個對象便可
obj.set_cookie('name','jason',max_age=30) return obj
參數:
其中還有刪除方法:
def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 刪除用戶瀏覽器上以前設置的usercookie值 return rep
def check_login(func): @wraps(func) def inner(request, *args, **kwargs): next_url = request.get_full_path() if request.get_signed_cookie("login", salt="SSS", default=None) == "yes": # 已經登陸的用戶... return func(request, *args, **kwargs) else: # 沒有登陸的用戶,跳轉剛到登陸頁面 return redirect("/login/?next={}".format(next_url)) return inner def login(request): if request.method == "POST": username = request.POST.get("username") passwd = request.POST.get("password") if username == "xxx" and passwd == "dashabi": next_url = request.GET.get("next") if next_url and next_url != "/logout/": response = redirect(next_url) else: response = redirect("/class_list/") response.set_signed_cookie("login", "yes", salt="SSS") return response return render(request, "login.html") cookie版登陸
七。session的刪除和設置。
session設置須要打開數據庫,首先生成數據庫文件:
# 獲取、設置、刪除Session中數據 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在則不設置 del request.session['k1']
而設置session通過了三件是,也就是session['key']= 'val'
1.django 內部自動生成一個隨機字符串
2.將隨機字符串和你要保存的數據 寫入django_session表中(如今內存中生成一個緩存記錄 等到通過中間件的時候纔會執行)
3.將產生的隨機字符串發送給瀏覽器寫入cookie
獲取session也會發生這幾件事:
1.django內部會自動從請求信息中獲取到隨機字符串
2.拿着隨機字符串去django_session表中比對
3.一旦對應上了就將對應的數據解析出來放到request.session中
session中的記錄默認是保存14tian,過時後數據不會消失,可是取值取不到。
當單個session設置了多個值時,值會保存到一個sessionid中,能夠正常取值。
django_session表中的一條記錄針對一個瀏覽器。
刪除信息。
刪除信息有兩種:
# 刪除當前會話的全部Session數據 request.session.delete() # 刪除當前的會話數據並刪除會話的Cookie。 request.session.flush() 這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問 例如,django.contrib.auth.logout() 函數中就會調用它。
delete是將瀏覽器中的session刪除。
而flush,刪除的是數據庫中的session和瀏覽器中的。
session也能夠設置超時時間:
# 設置會話Session和Cookie的超時時間 request.session.set_expiry(value) * 若是value是個整數,session會在些秒數後失效。 * 若是value是個datatime或timedelta,session就會在這個時間後失效。 * 若是value是0,用戶關閉瀏覽器session就會失效。 * 若是value是None,session會依賴全局session失效策略。
過時後數據不會消失,可是取值取不到。
在後期能夠將一些數據保存到session表中,保存的數據 能夠在後端任意位置獲取到。