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
步驟:
-
在app中建立templatetags模組
-
建立任意.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
-
在使用自定義simple_tag的html檔案中匯入之前建立的xx.py檔名
{% load xx %}
-
使用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 %}
-
在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的工作過程
- 生成隨機字串
- 寫到使用者瀏覽器的cookie中
- 儲存到session中
- 在隨機字串對應的字典中設定相關內容
而上述過程在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);
}
}
});
**總結 **
- csrf在ajax提交的時候通過請求頭傳遞的給後臺的
- csrf在前端的key為:X-CSRFtoken,到後端的時候django會自動新增HTTP_,並且最後為HTTP_X_CSRFtoken
- 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"<