1. 程式人生 > >Python3基礎之學習筆記 第十三天 Django

Python3基礎之學習筆記 第十三天 Django

1. Django

1.1 url補充

1.1.1 url預設值

path('index',views.index,{'name':'root'}),#設定name預設值為root
def index(request,name):#有預設值時函式必須有引數
    pass

1.1.2 名稱空間

from django.urls import path,include
urlpatterns=[
    path('a/',include('app01.urls',namespace='author-polls')),
    path('b/',include('app01.urls',namespace=
'publisher-polls')), ]
from django.urls import path,include
from . import views
app_name='app01'
urlpatterns=[
    path('detail',views.detail,name='detail'),
]
def detail(request,pk):
    print(request.resolver_match)
    return HttpResponse(pk)

以上定義帶名稱空間的url之後,使用name生成url時候必須加上namespace和name,應該如下:

  • v=reverse(‘author-polls:detail’,kwarrgs={‘pk’:11})
  • {% url ‘author-polls:detail’ pk=12 pp=99 %}

1.2 views獲取其他資訊

def index(request):
    #封裝了所有使用者請求資訊
    print(request.environ)
    #獲取cookies
    print(request.COOKIES)
    #獲取useragent
    print(request.environ['HTTP_USER_AGENT'])

1.3 自定義simple_tag和filter

步驟:

  1. 在app中建立templatetags模組

  2. 建立任意.py檔案,如果:xx.py

    from django import template
    from django.utils.safestring import mark_safe
    
    register = template.Library()
    
    @register.simple_tag
    def my_simple_time(a1,a2,a3):
        return a1 + a2+a3
    
    @register.simple_tag
    def my_input(id,arg):
        result="<input type='text' id='%s' class='%s' />"%(id,arg,)
        return mark_safe(result)
    @register.filter
    def aa(a1.a2):#最多隻能傳2個引數
        return a1+a2
    
  3. 在使用自定義simple_tag的html檔案中匯入之前建立的xx.py檔名

    {% load xx %}
    
  4. 使用simple_tag和filter

    {% my_simple_time 1 2 3 %}
    {% my_input 'id_username' 'hide' %}
    #使用filter
    {{ '引數1'| aa:'引數2'}}#結果為'引數1引數2'
    {% if {{ '引數1'| aa:'引數2'}} %}
    {% endif %}
    
  5. 在settings中註冊App

1.4 自定義分頁

1.4.1 防止xss

#前端程式碼
page_str="<input type='text' id='1' class='demo' />"
{{page_str|safe}}
#後端程式碼
from django.utils.safestring import mark_safe
page_str=mark_safe(page_str)

1.4.2 分頁函式

#!/usr/bin/python
#coding:utf-8
 
"""
@author: GoldenKitten
@contact: [email protected]
@software: PyCharm
@file: page.py
@time: 2018/9/17 14:29
"""
class Page(object):
    def __init__(self,
                 datas_count,
                 current_page=1,
                 per_page_data_count=10,
                 page_count=7):
        '''
        data_count:資料總共有多少條
        current_page:當前頁碼,預設從第一頁開始
        per_page_data_count:每一頁顯示多少條資料
        page_count:顯示多少個頁碼
        '''
        self.datas_count=datas_count
        self.current_page=current_page
        self.per_page_data_count=per_page_data_count
        self.page_count=page_count
    @property
    def total_page_count(self):
        # 獲取總頁數
        total_count, y = divmod(self.datas_count, self.per_page_data_count)
        if y:
            total_count += 1
        return total_count
    @property
    def start_page(self):
        #獲取開始的頁碼
        start=None
        if self.total_page_count<self.page_count:
            start= 1
        else:
            if self.current_page<=(self.page_count-1)/2:
                start= 1
            elif self.current_page>=(self.total_page_count-(self.page_count-1)/2):
                start=self.total_page_count-self.page_count+1
            else:
                start=self.current_page-(self.page_count-1)/2
        return int(start)
    @property
    def end_page(self):
        #獲取結尾時頁碼
        end=None
        if self.total_page_count<self.page_count:
            end=self.total_page_count
        else:
            if self.current_page<=(self.page_count-1)/2:
                end= self.page_count
            elif self.current_page>=(self.total_page_count-(self.page_count-1)/2):
                end= self.total_page_count
            else:
                end= self.current_page+(self.page_count-1)/2
        return int(end)
    def page(self):
        page_list=[]
        for i in range(self.start_page,self.end_page+1):
            page_list.append(i)
        return page_list
if __name__ == '__main__':
    p=Page(134,1)
    print(p.start_page)
    print(p.end_page)
    print(p.page())

1.5 cookie

1.5.1 簡單cookie

def login(req):
    if req.method=='GET':
        pass
    if req.method=='POST':
        u=req.POST.get('username')
        p=req.POST.get('password')
        res=HttpResponseRedirect('app2/host')
        res.set_cookie('username',u)#設定cookie
         #設定cookie5秒後失效
         #res.set_cookie('username',u,max_age=5)
        #設定超時時間
         #custom_datetime=datetime.datetime.utcnow()+datetime.timedelta(seconds=5)
         #res.set_cookie('username',u,expires=custom_datetime)
        return res
def index(req):
    if req.COOKIES.get('username'):#獲取cookie
        return HttpResponse('你已經登入過了')

設定cookie

rep = HttpResponse(...) 或 rep = render(request, ...)
 
rep.set_cookie(key,value,...)
#加密的cookie
rep.set_signed_cookie(key,value,salt='加密鹽',...)
req.get_signed_cookie(key,salt='加密鹽')
    引數:
        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獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)

1.6 CBV和FBV使用者認證裝飾器

FBV裝飾器

def auth(func):
    def inner(request,*args,**kwargs):
        u=request.COOKIES.get('username')
        if not u:
            return HttpResponseRedirect('/login')
        return func(request,*args,**kwargs)
    return inner

CBV裝飾器

from django.utils.decorators import method_decorator
def auth(func):
    def inner(request,*args,**kwargs):
        u=request.COOKIES.get('username')
        if not u:
            return HttpResponseRedirect('/login')
        return func(request,*args,**kwargs)
    return inner
class Order(views.View):
    @method_decorator(auth)#只裝飾get方法
    def get(self,request):
        pass
    def post(self,request):
        pass
#裝飾全部方法
@method_decorator(auth,name='dispatch')#只裝飾get方法
class Order(views.View):
    def get(self,request):
        pass
    def post(self,request):
        pass

1.7 Session

1.7.1 Session簡介

基於Cookie做使用者驗證時:敏感資訊不適合放在cookie中

Session原理:

  • Cookie是儲存在使用者瀏覽器端的鍵值對
  • Session是儲存在服務端的鍵值對

session的工作過程

  1. 生成隨機字串
  2. 寫到使用者瀏覽器的cookie中
  3. 儲存到session中
  4. 在隨機字串對應的字典中設定相關內容

而上述過程在Django中的體現為:

request.session[“username”]=user

這裡的username為通過request.POST.get(“username”)從前端html頁面中獲取到的使用者名稱資訊

注意:

在Django中要用session中一定要先執行:

python manage.py makemigrations

當用戶登入的時候的就會在資料庫的django_session表中記錄session資訊

同樣的通過request.session[“username”]也可以獲取相應的值

在這個過程中:

1、 首先獲取當前使用者的隨機字串

2、 根據隨機字串獲取對應的內容

1.7.2 Session簡單使用

def login(request):
    if request.method== 'GET':
        return render(request, 'app2/login.html')
    if request.method== 'POST':
        u=request.POST.get('user')
        p=request.POST.get('pwd')
        if u=='root' and p=='123':
            request.session['username']=u
            request.session['is_login']=True
            return HttpResponseRedirect('index')
        else:
            return render(request, 'app2/login.html')
def index(request):
    if request.session['is_login']:
        return HttpResponse('你已經登入過了')
    else:
        return HttpResponse('你沒有登入過')

1.7.3 Session操作

request.session["k1"]  如果不存在則會報錯
 
request.session.get["k1"],如果不存在則會報錯,為了防止出錯可以request.session.get('k1',None)
 
request.session['k1'] = 123 設定session值
 
request.session.setdefault('k1',123)  存在則不設定
 
del request.session['k1']  刪除
 
request.session.clear()    刪除
 
 
 
所有 鍵、值、鍵值對
 
request.session.keys()
 
request.session.values()
 
request.session.items()
 
request.session.iterkeys()
 
request.session.itervalues()
 
request.session.iteritems()
 
 
 
使用者session的隨機字串
 
request.session.session_key
 
 
將所有Session失效日期小於當前日期的資料刪除
 
request.session.clear_expired()
 
 
檢查 使用者session的隨機字串 在資料庫中是否
 
request.session.exists("session_key")
 
 
 
刪除當前使用者的所有Session資料
 
request.session.delete("session_key")
 
 
 
request.session.set_expiry(value)
 
預設的過期時間是兩週,如果自己設定了過期時間,這樣自己設定的優先順序就會高於預設的
 
如果value是個整數,session會在些秒數後失效。
 
如果value是個datatime或timedelta,session就會在這個時間後失效。
 
如果value是0,使用者關閉瀏覽器session就會失效。
 
如果value是None,session會依賴全域性session失效策略。
SESSION_COOKIE_NAME = "sessionid"      # Session的cookie儲存在瀏覽器上時的key,即:sessionid=隨機字串(預設)
 
SESSION_COOKIE_PATH = "/"              # Session的cookie儲存的路徑(預設)
 
SESSION_COOKIE_DOMAIN = None             # Session的cookie儲存的域名(預設)
 
SESSION_COOKIE_SECURE = False          # 是否Https傳輸cookie(預設)
 
SESSION_COOKIE_HTTPONLY = True         # 是否Session的cookie只支援http傳輸(預設)
 
SESSION_COOKIE_AGE = 1209600             # Session的cookie失效日期(2周)(預設)
 
SESSION_EXPIRE_AT_BROWSER_CLOSE = False    # 是否關閉瀏覽器使得Session過期(預設)
 
SESSION_SAVE_EVERY_REQUEST = False        # 是否每次請求都儲存Session,預設修改之後才儲存(預設)

Django中對於session的儲存方式

Django中支援session,其中內部提供了5種類型的session供開發者使用:

資料庫(預設)

快取

檔案

快取+資料庫

加密cookie

1、如果是資料庫,需要在settings.py中配置如下:

SESSION_ENGINE = ‘django.contrib.sessions.backends.db’ (引擎(預設))

2、如果是快取session,需要在settings.py中配置如下:

SESSION_ENGINE = ‘django.contrib.sessions.backends.cache’(引擎)

SESSION_CACHE_ALIAS= ‘default’ 使用的快取別名(預設記憶體快取,也可以是memcache),此處別名依賴快取的設定

1、 如果是檔案session, 需要在settings.py中配置如下:

SESSION_ENGINE = ‘django.contrib.sessions.backends.file’ (引擎)

SESSION_FILE_PATH=None 快取檔案路徑,如果為None,則使用tempfile模組獲取一個臨時地址tempfile.gettempdir()

2、 如果是快取+資料庫session,需要在settings.py中配置如下:

SESSION_ENGINE=‘django.contrib.sessions.backends.cached_db’ (引擎)

1.8 CSRF

1.8.1 簡介

django為使用者實現防止跨站請求偽造的功能,通過中介軟體 django.middleware.csrf.CsrfViewMiddleware 來完成。而對於django中設定防跨站請求偽造功能有分為全域性和區域性。

全域性:

中介軟體 django.middleware.csrf.CsrfViewMiddleware

區域性:

@csrf_protect,為當前函式強制設定防跨站請求偽造功能,即便settings中沒有設定全域性中介軟體。

@csrf_exempt,取消當前函式防跨站請求偽造功能,即便settings中設定了全域性中介軟體。

注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect

1.8.2 原理

當用post提交資料的時候,django會去檢查是否有一個csrf的隨機字串,如果沒有就會報錯,這也是之前我們一直將其註釋的原因 ,在django內部支援生成這個隨機字串 。

通過form提交

在form表單裡面需要新增{%csrf_token%}

這樣當你檢視頁面原始碼的時候,可以看到form中有一個input是隱藏的

<form action="/login/" method="POST">
        {% csrf_token %}
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="checkbox" name="rmb" value="1" /> 10秒免登入
        <input type="submit" value="提交" />
        <input id="btn1" type="button" value="按鈕" />
        <input id="btn2" type="button" value="按鈕" />
    </form>

總結原理:當用戶訪問login頁面的時候,會生成一個csrf的隨機字串,,並且cookie中也存放了這個隨機字串,當用戶再次提交資料的時候會帶著這個隨機字串提交,如果沒有這個隨機字串則無法提交成功

通過ajax提交

因為cookie中同樣存在csrftoken,所以可以在js中通過:

$.cooke(“cstftoken”)獲取

如果通過ajax進行提交資料,這裡提交的csrftoken是通過請求頭中存放,需要提交一個字典型別的資料,即這個時候需要一個key。

在views中的login函式中:from django.conf import settings,然後列印print(settings.CSRF_HEADER_NAME)

這裡需要注意一個問題,這裡匯入的settings並不是我們在專案檔案下看到的settings.py檔案,這裡是是一個全域性的settings配置,而當我們在專案目錄下的settings.py中配置的時候,我們新增的配置則會覆蓋全域性settings中的配置

print(settings.CSRF_HEADER_NAME)列印的內容為:HTTP_X_CSRFTOKEN

這裡的HTTP_X_CSRFTOKEN是django在X_CSRF的前面添加了HTTP_,所以實際傳遞的是就是X_CSRFtoken,而在前端頁面的ajax傳遞的時候由於不能使用下劃線所以傳遞的是X_CSRFtoken

下面是在前端ajax中寫的具體內容:

$("#btn1").click(function () {
        $.ajax({
            url:"/login/",
            type:"POST",
            data:{"usr":"root","pwd":"123"},
            headers:{ "X-CSRFtoken":$.cookie("csrftoken")},
            success:function (arg) {
 
            }
        })
    })

但是如果頁面中有多個ajax請求的話就在每個ajax中新增headers資訊,所以可以通過下面方式在所有的ajax中都新增

$.ajaxSetup({
            beforeSend:function (xhr,settings) {
                xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken"))
            }
        });

這樣就會在提交ajax之前執行這個方法,從而在所有的ajax裡都加上這個csrftoken

這裡的xhr是XMLHttpRequest的簡寫,ajax呼叫的就是這個方法

如果想要實現在當get方式的時候不需要提交csrftoken,當post的時候需要,實現這種效果的程式碼如下:

function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });

**總結 **

  1. csrf在ajax提交的時候通過請求頭傳遞的給後臺的
  2. csrf在前端的key為:X-CSRFtoken,到後端的時候django會自動新增HTTP_,並且最後為HTTP_X_CSRFtoken
  3. csrf在form中提交的時需要在前端form中新增{%csrftoken%}

1.9 中介軟體

中介軟體顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全域性上改變django的輸入與輸出。因為改變的是全域性,所以需要謹慎實用,用不好會影響到效能。  每個中介軟體都會負責一個功能,例如,AuthenticationMiddleware,與sessions處理相關。

一般我們我們從瀏覽器發出一個請求 Request,得到一個響應後的內容 HttpResponse ,這個請求傳遞到 Django的過程 也就是說,每一個請求都是先通過中介軟體中的 process_request 函式,這個函式返回 None 或者 HttpResponse 物件,如果返回前者,繼續處理其它中介軟體,如果返回一個 HttpResponse,就處理中止,返回到網頁上。

中介軟體寫法

try:
    from django.utils.deprecation import MiddlewareMixin  # Django 1.10.x
except ImportError:
    MiddlewareMixin = object  # Django 1.4.x - Django 1.9.x
 
class SimpleMiddleware(MiddlewareMixin):
    def process_request(self, request):
        pass
     def process_view(self,request,view_func,view_func_args,view_func_kwargs):
        pass
    def process_response(self,request,response):
        return response

中介軟體中可以定義五個方法 :

process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
#前二個方法是從前往後執行的,後三個方法是從後往前執行的

簡單識別手機的中介軟體

MOBILE_USERAGENTS = ("2.0 MMP","240x320","400X240","AvantGo","BlackBerry",
    "Blazer","Cellphone","Danger","DoCoMo","Elaine/3.0","EudoraWeb",
    "Googlebot-Mobile","hiptop","IEMobile","KYOCERA/WX310K","LG/U990"<