多對多的三種建立方式-forms相關組件-鉤子函數-cookie與session

多對多的三種建立方式

  一、全自動(推薦使用的**)css

優點:第三張能夠任意的擴展字段html

缺點:ORM查詢不方便,若是後續字段增長更改時不便添加修改前端

  manyToManyField建立的第三張表屬於虛擬的,後綴會自動添加有_id的外鍵字段python

建立的方式:jquery

 

   二、純手動(不推薦使用)正則表達式

  須要手動創第三方表數據庫

優點:第三張表能夠任意的擴展字段django

缺點:ORM查詢不便後端

 

   三、半自動(推薦使用***)瀏覽器

優點:結合了全自動和半自動的兩個優勢,把建表的關係直接在一張表上表示出來

 

 這樣建立的表,在多對多時不支持ORM的操做有如下幾種:

"""
   多對多字段的
   add
   set
   remove
   clear不支持
   
"""

 Form組件

  forms組件功能

  一、校驗功能:

  • 就是將form表單中的值在post請求發送到服務端時,服務端利用forms組件去檢驗是否符合規則
  • form表單中的name屬性值要和自定義forms組件的字段一致。

  二、標籤的渲染功能

  三、渲染錯誤的信息

  四、局部鉤子,再次檢驗

使用forms組件是實現註冊功能

簡單的註冊功能的版本:

  實現簡單的校驗功能。設置條件,在註冊的時候進行有效的檢驗

views.py

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組件的基本用法

基本的校驗規則:

  先寫好一個繼承了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的校驗規則:根據字段一個個進行比對,符合條件和錯誤的分別的保存起來

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組件不會去校驗 也就意味着多傳一點關係沒有

在書寫的字段內;只能多傳不能少傳。

 forms三種渲染方式

  書寫from表單的三種方法:

前端簡單的from表單以下,原始的

 

 

 在Django中利用forms組件書寫的from表單又如下三種方法:

先在後端開啓路由向前端傳遞數據信息,views.py

def register(request):
    # 先生成一個空白的自定義類對象
    form_obj = LoginForm()
    # 將該對象傳遞給前端
    return  render(requesr,'register.html')

前端書寫的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>

一、第一種渲染頁面的方式(封裝程度過高 通常只用於本地測試  一般不適

二、<p>第二種渲染頁面的方式(可擴展性較高 書寫麻煩)</p>

三、<p>第三種渲染頁面的方式(推薦)</p>

 forms組件展現錯誤的信息提示

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註釋 ,不指定的狀況下,默認用的類中字段的首字母大寫

 關鍵字:novalidate

  告訴前端取消掉自帶的校驗方式,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'郵箱'}))
自定義的驗證函數方法

鉤子函數(HOOK)

  就是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

 

forms組件其餘字段及其餘操做的使用

""" 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開頭')],
    )

 

具體的展現:

 

forms組件的源碼分析

  經過源碼解析,看Django內部的forms組件是如何是實現校驗的原理的;大體瞭解

 

 點擊進去查看is_valid()方法

 

核心精髓實現功能的代碼部分

Cookie與Session組件

  1、cookie:

  因爲Http協議的無狀態的特色,也就是說即便第一次和服務器鏈接後而且登陸成功後,

第二次請求服務器依然不能知道當前請求是哪一個用戶,cookies的出現就是爲了解決這一現象。

cooki就是保存在客戶端瀏覽器上的鍵值對:

工做原理:當用戶登陸成功後,瀏覽器上會保存一些信息,下次再來訪問的時候,就會攜帶着這些信息去訪問服務端,服務端經過這些信息來識別出你的身份

  2、session:

  sessioncookie的做用有點相似,都是爲了存儲用戶相關的信息。不一樣的是,cookie是存儲在本地瀏覽器,而session存儲在服務器。存儲在服務器的數據會更加的安全,

不容易被竊取。但存儲在服務器也有必定的弊端,就是會佔用服務器的資源,但如今服務器已經發展至今,一些session信息仍是綽綽有餘的。

   session就是保存在服務器上的鍵值對
   session雖然是保存在服務器上的鍵值對
   可是它是依賴於cookie工做的

cookie就是保存在客戶端瀏覽器上的鍵值對,點擊查看已經登陸了的Cookie,會自動保存一些信息

 

 

 查看cookie的方法

 

 如何操做cookie和session

  前提分析:Django返回給客戶端瀏覽器的都必須是HttpResponse對象;

三板斧:

  return HttpResonse(): 返回字符串

  return render()   返回html頁面

  return  redirect()  重定向到...

Django操做cookie

  經過對cookie的操做,讓服務器如何存儲數據信息,保存用戶的登陸狀態,簡單的寫一個都登陸註冊的例子:

首先先掌握如何操做cookie:

獲取cookie的方法

  request.COOKIE.get()

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

參數:

  • default: 默認值
  • salt: 加密鹽
  • max_age: 後臺控制過時時間

設置Cookie

   set_cookie

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

 

cookie登陸版的校驗

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的操做

  獲取、設置、刪除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所攜帶的表字段,其中的時間exprie_time採用的是UTC時間,相差個小時

Django默認的超時實際時間是14天

  重點掌握設置和獲取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表中,保存的數據 能夠在後端任意位置獲取到

相關文章
相關標籤/搜索