Web框架之Django_08 重要組件(form組件、cookie和session組件)

摘要:

  • form組件

  • cookie組件

  • session組件

1、form組件

  • form介紹
    咱們以前在html頁面中利用form表單向後端提交數據時候,都須要對用戶的輸入進行校驗,好比校驗用戶是否輸入正確(長度、格式...),若是用戶輸入的內容有誤則須要在相應的位置顯示對應的錯誤信息來提醒用戶,提升前端的交互效率。
    Django form組件就是實現上述功能的一個功能組件。
    from組件主要功能有:
    # 生成頁面可用的html標籤
    # 對用戶條件的數據進行校驗
    # 保留上次輸入內容
  • 先來看看本身手寫註冊功能的過程:
    # views.py
    # 註冊
    def register(request):
        error_msg = ""
        if request.method == "POST":
            username = request.POST.get("name")
            pwd = request.POST.get("pwd")
            # 對註冊信息作校驗
            if len(username) < 6:
                # 用戶長度小於6位
                error_msg = "用戶名長度不能小於6位"
            else:
                # 將用戶名和密碼存到數據庫
                return HttpResponse("註冊成功")
        return render(request, "register.html", {"error_msg": error_msg})
    
    # register.html
    !DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>註冊頁面</title>
    </head>
    <body>
    <form action="/reg/" method="post">
        <p>
            用戶名:
            <input type="text" name="name">
        </p>
        <p>
            密碼:
            <input type="password" name="pwd">
        </p>
        <p>
            <input type="submit" value="註冊">
            <p style="color: red">{{ error_msg }}</p>
        </p>
    </form>
    </body>
    </html>
  • 校驗數據
    使用form組件實現註冊功能:html

    from django import forms
    # 按照Django form組件的要求本身寫一個類
    class MyForm(forms.Form):
        name = forms.CharField(label='用戶名', max_length=6)
        pwd = forms.CharField(label='密碼', max_length=8, min_length=4)
    
    def register(request):
        form_obj = MyForm()
        if request.method == 'POST':
            # 實例化form對象的時候,把post提交過來的數據直接傳進去
            form_obj = MyForm(request.POST)
            # 調用form_obj校驗數據的方法
            if form_obj.is_valid():  # 這裏is_valid()若是驗證所有經過,則返回True
                return HttpResponse('註冊成功')
        return render(request, 'register.html', {'form_obj': form_obj})

    這裏咱們使用PythonConsole來測試:前端

    依次輸入:(帶#的爲結果)
    from app01 import views
    form_obj = views.MyForm({'name': 'hhh', 'pwd': '123'})
    form_obj.is_valid()
    #False
    form_obj.errors
    #{'pwd': ['Ensure this value has at least 4 characters (it has 3).']}
    form_obj.cleaned_data
    #{'name': 'hhh'}

    ## form_obj.is_valid()  校驗提交的信息,若是所有經過,則返回True,不然爲False
    ## from_obj.errors 查看錯誤的信息,結果是一個字典格式,key爲錯誤的字段,value爲錯誤的緣由,注意這裏面是一個列表,說明緣由能夠有多個,這裏須要說明:全部校驗未經過的字段及錯誤信息提示都在這裏放着,以鍵值對的形式存放。
    ## form_obj.cleaned_data  查看校驗經過的數據,這裏存放這全部校驗經過的字段及其值,以字典形式存放。
    特別補充:
    若是:多串字段:不進行校驗,因此不會在cleaned_data和errors中
               少傳字段: 也會校驗少傳的字段,會在errors中,且該字段描述:{'pwd': ['This field is required.']}
    全部Form中的字段默認都是要校驗,可是能夠經過設置required = False來更過。git

  • 渲染標籤
    # views
    from django import forms
    # 按照Django form組件的要求本身寫一個類
    class MyForm(forms.Form):
        name = forms.CharField(max_length=6)
        pwd = forms.CharField(max_length=8, min_length=4)
    
    def register(request):
        form_obj = MyForm()
        if request.method == 'POST':
            # 實例化form對象的時候,把post提交過來的數據直接傳進去
            form_obj = MyForm(request.POST)
            # 調用form_obj校驗數據的方法
            if form_obj.is_valid():  # 這裏is_valid()若是驗證所有經過,則返回True
                return HttpResponse('註冊成功')
        return render(request, 'register.html', {'form_obj': form_obj})
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/register/" method="post">
    {#    .as_p表明將全部的字段都對應input框渲染給前端顯示#}
        {{ form_obj.as_p }}
    
    </form>
    </body>
    </html>

    能夠看到,前面的Name和Pwd是Django本身渲染加上的,若是想要自定義,能夠在MyForm類的對應字段里加上:label='用戶名'和label='密碼'便可正則表達式

    上面是第一種渲染方式,能夠看出可拓展性較差數據庫

  • 第二種渲染方式:
    多加幾個校驗字段
    from django import forms
    # 按照Django form組件的要求本身寫一個類
    class MyForm(forms.Form):
        name = forms.CharField(max_length=6, label='用戶名')
        pwd = forms.CharField(max_length=8, min_length=4, label='密碼')
        email = forms.EmailField(label='郵箱')
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/register/" method="post">
    <p>{{ form_obj.name }}</p>
    <p>{{ form_obj.pwd }}</p>
    <p>{{ form_obj.email }}</p>
    </form>
    </body>
    </html>

    只有input框!
    加入input框前的名字:django

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/register/" method="post">
    <p>{{ form_obj.name.label }}{{ form_obj.name }}</p>
    <p>{{ form_obj.pwd.label }}{{ form_obj.pwd }}</p>
    <p>{{ form_obj.email.label }}{{ form_obj.email }}</p>
    </form>
    </body>
    </html>

    此種方法仍是有缺點,好比字段若是有100個,難道要100個所有輸嗎?因此應該還有更好的方法:後端

  • 第三種渲染方式:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/register/" method="post">
    {% for foo in form_obj %}
        <p>第三種渲染方式:{{ foo.label }}{{ foo }}</p>
    {% endfor %}
    </form>
    </body>
    </html>

    這裏提一下:若是想要取消前端校驗,能夠在form表單中加入:novalidate
    這裏須要知道:
        一、若是Django建立的form表單在提交的時候數據不合法(校驗不經過),則會自動保留填寫的信息,不會清空。
        二、Django建立的表單是沒有提交按鈕的,須要本身建立。
    接下來,添加校驗不經過時候的提示信息:只需在恰當的位置添加瀏覽器

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/register/" method="post" novalidate>
    {% for foo in form_obj %}
        <p>第三種渲染方式:{{ foo.label }}{{ foo }}<span>{{ foo.errors }}</span></p>
    {% endfor %}
        <input type="submit">
    </form>
    </body>
    </html>

    這裏,提示錯誤信息在下面顯示,若是想讓它在每一個字段的input框後面顯示能夠加個0安全

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/register/" method="post" novalidate>
    {% for foo in form_obj %}
        <p>第三種渲染方式:{{ foo.label }}{{ foo }}<span>{{ foo.errors.0 }}</span></p>
    
    {% endfor %}
        <input type="submit">
    </form>
    </body>
    </html>

    繼續走向個性化:提示的錯誤信息是英文,我看不懂,那就改爲中文:
    修改錯誤提示信息爲自定義信息服務器

    from django import forms
    # 按照Django form組件的要求本身寫一個類
    class MyForm(forms.Form):
        name = forms.CharField(max_length=6, label='用戶名', error_messages={
            'max_length': '用戶名最大長度爲6位',
            'required': '用戶名必須不能爲空'
        })
        pwd = forms.CharField(max_length=8, min_length=4, label='密碼', error_messages={
            'max_length': '密碼最大長度爲8位',
            'min_length': '密碼最小長度爲4位',
            'required': '用戶名必須不能爲空'
        })
        email = forms.EmailField(label='郵箱', error_messages={
            'required': '郵箱必須不能爲空',
            'invalid': '郵箱格式不合法'
        })

    測試測試:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="/register/" method="post" novalidate>
    {% for foo in form_obj %}
        <p>第三種渲染方式:{{ foo.label }}{{ foo }}<span>{{ foo.errors.0 }}</span></p>
    
    {% endfor %}
        <input type="submit">
    </form>
    </body>
    </html>

    這裏有個小點須要說明:默認google瀏覽器的表單輸入框若是要求最大6位,就只能輸入6位最大,再多輸入沒有反應,這個爲了測試方便,能夠認爲在每次提交前經過右鍵檢查,人爲刪除限制的html代碼

  • 經常使用字段與插件:
    這裏系統的在補充一些Form類在建立的時候會涉及到的字段和插件,自對用於對用戶請求數據的驗證,插件用於自動生成html

    initial

    初始值,input框裏面的初始值。

    class LoginForm(forms.Form):
        username = forms.CharField(
            min_length=8,
            label="用戶名",
            initial="張三"  # 設置默認值
        )
        pwd = forms.CharField(min_length=6, label="密碼")

    error_messages(這個上面以提到)

    重寫錯誤信息。

    class LoginForm(forms.Form):
        username = forms.CharField(
            min_length=8,
            label="用戶名",
            initial="張三",
            error_messages={
                "required": "不能爲空",
                "invalid": "格式錯誤",
                "min_length": "用戶名最短8位"
            }
        )
        pwd = forms.CharField(min_length=6, label="密碼")

    PasswordInput
    給對應字段的input框設置屬性,類....

        pwd = forms.CharField(max_length=8, min_length=4, label='密碼', error_messages={
            'max_length': '密碼最大長度爲8位',
            'min_length': '密碼最小長度爲4位',
            'required': '用戶名必須不能爲空'
        }, widget=forms.widgets.PasswordInput(attrs={'type': 'password'}))
    
    # 將input改爲password類型,這樣輸入的就不是明文了

    radioSelect

    將下拉式選擇框轉變成單選形式

        gender = forms.ChoiceField(
            choices=((1, ""), (2, ""), (3, "保密")),
            label="性別",
            initial=3,
            widget=forms.widgets.RadioSelect()

    單選Select

    class LoginForm(forms.Form):
        ...
        hobby = forms.ChoiceField(
            choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ),
            label="愛好",
            initial=3,
            widget=forms.widgets.Select()
        )

    多選Select

    class LoginForm(forms.Form):
        ...
        hobby = forms.MultipleChoiceField(
            choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ),
            label="愛好",
            initial=[1, 3],
            widget=forms.widgets.SelectMultiple()
        )

    單選checkbox

    class LoginForm(forms.Form):
        ...
        keep = forms.ChoiceField(
            label="是否記住密碼",
            initial="checked",
            widget=forms.widgets.CheckboxInput()
        )

    多選checkbox

    class LoginForm(forms.Form):
        ...
        hobby = forms.MultipleChoiceField(
            choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),
            label="愛好",
            initial=[1, 3],
            widget=forms.widgets.CheckboxSelectMultiple()
        )

    choice字段注意事項

    在使用選擇標籤時,須要注意choices的選項能夠配置從數據庫中獲取,可是因爲是靜態字段 獲取的值沒法實時更新,須要重寫構造方法從而實現choice實時更新。

    方式一:

    from django.forms import Form
    from django.forms import widgets
    from django.forms import fields
    
     
    class MyForm(Form):
     
        user = fields.ChoiceField(
            # choices=((1, '上海'), (2, '北京'),),
            initial=2,
            widget=widgets.Select
        )
     
        def __init__(self, *args, **kwargs):
            super(MyForm,self).__init__(*args, **kwargs)
            # self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
            # 或
            self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')

    方式二:

    from django import forms
    from django.forms import fields
    from django.forms import models as form_model
    
     
    class FInfo(forms.Form):
        authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多選
        # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())  # 單選
  • DjangoForm全部內置字段
    Field
        required=True,               是否容許爲空
        widget=None,                 HTML插件
        label=None,                  用於生成Label標籤或顯示內容
        initial=None,                初始值
        help_text='',                幫助信息(在標籤旁邊顯示)
        error_messages=None,         錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'}
        validators=[],               自定義驗證規則
        localize=False,              是否支持本地化
        disabled=False,              是否能夠編輯
        label_suffix=None            Label內容後綴
     
     
    CharField(Field)
        max_length=None,             最大長度
        min_length=None,             最小長度
        strip=True                   是否移除用戶輸入空白
     
    IntegerField(Field)
        max_value=None,              最大值
        min_value=None,              最小值
     
    FloatField(IntegerField)
        ...
     
    DecimalField(IntegerField)
        max_value=None,              最大值
        min_value=None,              最小值
        max_digits=None,             總長度
        decimal_places=None,         小數位長度
     
    BaseTemporalField(Field)
        input_formats=None          時間格式化   
     
    DateField(BaseTemporalField)    格式:2015-09-01
    TimeField(BaseTemporalField)    格式:11:12
    DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
     
    DurationField(Field)            時間間隔:%d %H:%M:%S.%f
        ...
     
    RegexField(CharField)
        regex,                      自定製正則表達式
        max_length=None,            最大長度
        min_length=None,            最小長度
        error_message=None,         忽略,錯誤信息使用 error_messages={'invalid': '...'}
     
    EmailField(CharField)      
        ...
     
    FileField(Field)
        allow_empty_file=False     是否容許空文件
     
    ImageField(FileField)      
        ...
        注:須要PIL模塊,pip3 install Pillow
        以上兩個字典使用時,須要注意兩點:
            - form表單中 enctype="multipart/form-data"
            - view函數中 obj = MyForm(request.POST, request.FILES)
     
    URLField(Field)
        ...
     
     
    BooleanField(Field)  
        ...
     
    NullBooleanField(BooleanField)
        ...
     
    ChoiceField(Field)
        ...
        choices=(),                選項,如:choices = ((0,'上海'),(1,'北京'),)
        required=True,             是否必填
        widget=None,               插件,默認select插件
        label=None,                Label內容
        initial=None,              初始值
        help_text='',              幫助提示
     
     
    ModelChoiceField(ChoiceField)
        ...                        django.forms.models.ModelChoiceField
        queryset,                  # 查詢數據庫中的數據
        empty_label="---------",   # 默認空顯示內容
        to_field_name=None,        # HTML中value的值對應的字段
        limit_choices_to=None      # ModelForm中對queryset二次篩選
         
    ModelMultipleChoiceField(ModelChoiceField)
        ...                        django.forms.models.ModelMultipleChoiceField
     
     
         
    TypedChoiceField(ChoiceField)
        coerce = lambda val: val   對選中的值進行一次轉換
        empty_value= ''            空值的默認值
     
    MultipleChoiceField(ChoiceField)
        ...
     
    TypedMultipleChoiceField(MultipleChoiceField)
        coerce = lambda val: val   對選中的每個值進行一次轉換
        empty_value= ''            空值的默認值
     
    ComboField(Field)
        fields=()                  使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式
                                   fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
     
    MultiValueField(Field)
        PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用
     
    SplitDateTimeField(MultiValueField)
        input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
        input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
     
    FilePathField(ChoiceField)     文件選項,目錄下文件顯示在頁面中
        path,                      文件夾路徑
        match=None,                正則匹配
        recursive=False,           遞歸下面的文件夾
        allow_files=True,          容許文件
        allow_folders=False,       容許文件夾
        required=True,
        widget=None,
        label=None,
        initial=None,
        help_text=''
     
    GenericIPAddressField
        protocol='both',           both,ipv4,ipv6支持的IP格式
        unpack_ipv4=False          解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用
     
    SlugField(CharField)           數字,字母,下劃線,減號(連字符)
        ...
     
    UUIDField(CharField)           uuid類型
    
    
    Django Form內置字段
    全部內置字段
  • Hook方法(鉤子函數)
    鉤子函數分爲:局部鉤子和全局鉤子
    這裏還須要注意:鉤子函數是在對校驗經過的字段進行進一步校驗。因此須要寫在字段下面

    局部鉤子

    咱們在Fom類中定義 clean_字段名() 方法,就可以實現對特定字段進行校驗。

    # 局部鉤子
        def clean_name(self):
            name = self.cleaned_data.get('name')
            if '666' in name:
                # 用add_error添加錯誤提示
                self.add_error('name', '666不對')
            return name

    全局鉤子

    咱們在Fom類中定義 clean() 方法,就可以實現對字段進行全局校驗。

    # 輸入兩次密碼一致性的全局鉤子
    設置兩個密碼字段
        pwd = forms.CharField(max_length=8, min_length=4, label='密碼', error_messages={
            'max_length': '密碼最大長度爲8位',
            'min_length': '密碼最小長度爲4位',
            'required': '用戶名必須不能爲空'
        }, widget=forms.widgets.PasswordInput(attrs={'type': 'password'}))
        confirm_pwd = forms.CharField(max_length=8, min_length=4, label='密碼', error_messages={
            'max_length': '密碼最大長度爲8位',
            'min_length': '密碼最小長度爲4位',
            'required': '用戶名必須不能爲空'
        }, widget=forms.widgets.PasswordInput(attrs={'type': 'password'}))
    
    # 全局鉤子:
        def clean(self):
            pwd = self.cleaned_data.get('pwd')
            confirm_pwd = self.cleaned_data.get('confirm_pwd')
            if pwd != confirm_pwd:
                self.add_error('confirm_pwd', '密碼兩次輸入不一致')
            return self.cleaned_data

  • 最後說說註冊的實際流程:也就是咱們經過校驗成功後如何將註冊信息存入數據庫:
    def reg(request):
        # 生成一個空對象
        form_obj = MyForm()
        if request.method == 'POST':
            print(request.POST)
            form_obj = MyForm(request.POST)
            if form_obj.is_valid():
                # print(form_obj.cleaned_data)
                # 這裏打散傳值須要注意的是模型表字段名要與模型中的字段名相同,否則確定會報錯。
                # 因此平時要養成習慣,不少地方須要同樣,以便於代碼的可讀性
                # models.User.objects.create(**form_obj.cleaned_data)
        return render(request,'reg.html',locals())

 2、cookie與session

  • Cookie介紹

    Cookie的由來

    你們都知道HTTP協議是無狀態的。

    無狀態的意思是每次請求都是獨立的,它的執行狀況和結果與前面的請求和以後的請求都無直接關係,它不會受前面的請求響應狀況直接影響,也不會直接影響後面的請求響應狀況。

    一句有意思的話來描述就是人生只如初見,對服務器來講,每次的請求都是全新的。

    狀態能夠理解爲客戶端和服務器在某次會話中產生的數據,那無狀態的就覺得這些數據不會被保留。會話中產生的數據又是咱們須要保存的,也就是說要「保持狀態」。所以Cookie就是在這樣一個場景下誕生。

    什麼是Cookie

    Cookie具體指的是一段小信息,它是服務器發送出來存儲在瀏覽器上的一組組鍵值對,下次訪問服務器時瀏覽器會自動攜帶這些鍵值對,以便服務器提取有用信息。

    Cookie的原理

    cookie的工做原理是:由服務器產生內容,瀏覽器收到請求後保存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上Cookie,這樣服務器就能經過Cookie的內容來判斷這個是「誰」了。

    查看Cookie

    咱們使用Chrome瀏覽器,打開開發者工具。

    還能夠在這裏查看:

    Django中操做Cookie

    前提:咱們在後端views中視圖函數的返回參數render(),HttpResponse(),redirect(),經過查看它們的源碼發現加括號後世界上返回的是一個對象,也就是咱們返回到前端的是一個對象,之前咱們是直接返回,如今要用到cookie和session就得在返回前對這個對象進行操做
    設置Cookie

    # views
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'sgt' and password == '123':
                # 用戶登陸校驗成功,下一步設置cookie,須要知道用哪個網頁使用cookie
                obj = redirect('/index/')
                # 設置cookie
                obj.set_cookie('name', 'sgt', expires=15)  # 過時時間15秒
                return obj
                # 這個obj就是進行操做,設置好cookie的redirect('/index/')
        return render(request, 'login.html')
    
    
    def index(request):
        return render(request, 'index.html')
    
    # 在login.html登陸頁面,進行登陸,若是用戶名和密碼爲sgt和123,則跳轉到inde.html,同時在跳轉以前設置cookie,經過在index頁面右鍵檢查,查看cookie能夠看到cookie也就存在,說明設置成功

    固然上面的設置cookie爲了演示建立過程,並無加任何安全措施,在實際使用中咱們確定會對他進行加密處理:作一下更安全性的操做處理:

    obj.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)
    參數:
    key, 鍵
    value='', 值
    max_age=None, 超時時間
    expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
    path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie能夠被任何url的頁面訪問
    domain=None, Cookie生效的域名
    secure=False, https傳輸
    httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)

    獲取Cookie

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

    參數:

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

    刪除Cookie

    def logout(request):
        obj = redirect("/login/")
        obj.delete_cookie("user")  # 刪除用戶瀏覽器上以前設置的usercookie值
        return rep
    只有已經登陸了,才能訪問網頁,不然跳轉到登陸頁面,
    經過cookie來校驗登陸,同時經過登陸裝飾器來校驗登陸,實現登陸後自動跳轉到原來須要提示登陸的網頁
    # 登陸頁面視圖函數
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'jason' and password == '123':
                old_path = request.GET.get('next')
                if old_path:
                    # 若是原來頁面有,則自動跳轉
                    obj = redirect(old_path)
                else:
                    # 若是是直接進入登陸頁面,不是從其它頁面跳轉的,則默認跳到指定主頁面去
                    obj = redirect('/home/')
                # 用戶登陸成功 朝瀏覽器設置一個cookie
                obj.set_cookie('name','jason',expires=3600) # 這個兼容性更高
                # obj.set_cookie('name','jason',max_age=5)
                return obj
        return render(request,'login.html')
    
    # 登陸驗證裝飾器    
    from functools import wraps
    def login_auth(func):
        @wraps(func)
        def inner(request,*args,**kwargs):
            # 校驗cookie
            # print(request.get_full_path())
            old_path = request.get_full_path()  # 拿到原來須要登陸的頁面路由
            if request.COOKIES.get('name'):
                return func(request,*args,**kwargs)
            # 若是未登陸,跳轉到登陸頁面,同時在路由後面拼接老頁面路由地址,用於登陸成功自動跳轉
            return redirect('/login/?next=%s'%old_path)
        return inner
    
    
    @login_auth
    def index(request):
        # # print(request.COOKIES.get('name'))
        # if request.COOKIES.get('name'):
        return HttpResponse('我是index頁面,只有登陸了才能看')
    
    
    @login_auth
    def home(request):
        return HttpResponse('我是home頁面,只有登陸了才能看')
    
    @login_auth
    def xxx(request):
        return HttpResponse('我是xxx頁面,只有登陸了才能看')
  • Session介紹

    Cookie雖然在必定程度上解決了「保持狀態」的需求,可是因爲Cookie自己最大支持4096字節,以及Cookie自己保存在客戶端,可能被攔截或竊取,所以就須要有一種新的東西,它能支持更多的字節,而且他保存在服務器,有較高的安全性。這就是Session。

    問題來了,基於HTTP協議的無狀態特徵,服務器根本就不知道訪問者是「誰」。那麼上述的Cookie就起到橋接的做用。

    咱們能夠給每一個客戶端的Cookie分配一個惟一的id,這樣用戶在訪問時,經過Cookie,服務器就知道來的人是「誰」。而後咱們再根據不一樣的Cookie的id,在服務器上保存一段時間的私密資料,如「帳號密碼」等等。

    總結而言:Cookie彌補了HTTP無狀態的不足,讓服務器知道來的人是「誰」;可是Cookie以文本的形式保存在本地,自身安全性較差;因此咱們就經過Cookie識別不一樣的用戶,對應的在Session裏保存私密的信息以及超過4096字節的文本。

    另外,上述所說的Cookie和Session實際上是共通性的東西,不限於語言和框架。

  • session基本使用(設置,獲取)
    # 設置session:

    request.session['name']='jason'

    設置session時候Django作了3件事:
    ①生成一個隨機的字符串
    ②在Django的session表存儲隨機字符串與數據記錄
    ③將隨機字符串發送給瀏覽器
    # 獲取session:

    request.session.get('name')
  • # Django中session的相關方法:

    # 獲取、設置、刪除Session中數據
    request.session['k1']
    request.session.get('k1',None)
    request.session['k1'] = 123
    request.session.setdefault('k1',123) # 存在則不設置
    del request.session['k1']
    
    
    # 全部 鍵、值、鍵值對
    request.session.keys()
    request.session.values()
    request.session.items()
    request.session.iterkeys()
    request.session.itervalues()
    request.session.iteritems()
    
    # 會話session的key
    request.session.session_key
    
    # 將全部Session失效日期小於當前日期的數據刪除
    request.session.clear_expired()
    
    # 檢查會話session的key在數據庫中是否存在
    request.session.exists("session_key")
    
    # 刪除當前會話的全部Session數據
    request.session.delete()
      
    # 刪除當前的會話數據並刪除會話的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解析
     

  • session版驗證登陸

    # session版登陸
    def login(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'sgt' and password == '123456':
                # 設置session
                request.session['username'] = username
                # 跳轉到登陸頁面以前的URL
                next_url = request.GET.get('next')
                # 這裏須要判斷一下用戶是不是跳轉過來登陸的仍是直接登陸的
                if next_url:  # 若是有則跳轉到原頁面
                    return redirect(next_url)
                else:  # 不然默認指定跳轉到指定頁面
                    return redirect('/index/')
            return render(request, 'login.html')
    
    # 登陸驗證裝飾器:
    # 登陸驗證的過程其實是基於已登陸的狀態進行校驗的,也就是說,用戶登陸後,
    # 會在服務端生成一個cookie存儲下來,同時把一份cookie再發給瀏覽器,
    # 當用戶在某個跳轉到某個須要登陸次才能訪問的頁面時候,就會進行登陸校驗過程
    # 會將瀏覽器的cookie發到服務端,和服務端存儲的cookie進行匹配,若是匹配到,就說明
    # 服務端已經記錄這這個用戶的登陸狀態,容許訪問。
    
    
    from functools import wraps
    def login_auth(func):
        def inner(request, *args, **kwargs):
            # 獲取到驗證登陸的當前頁面的所有路由
            next_url = request.get_full_path()
            # 若是改用戶的session存在,說明已登陸
            if request.session.get('username'):
                return func(request, *args, **kwargs)
            # 發現未登陸,則跳轉到登陸頁面,同時路由後面拼接當前頁面路由
            else:
                return redirect('/login/?next=%s' % next_url)
        return inner
    
    @login_auth
    def logout(request):
        # 刪除全部當前請求相關的session
        request.session.delete()
        return redirect("/login/")
    
    
    @login_auth
    def index(request):
        current_user = request.session.get("user", None)
        return render(request, "index.html", {"user": current_user})
相關文章
相關標籤/搜索