simple_tag,filte,分頁以及cookie和裝飾器,session
自定義simple_tag
內建的方法
首先Django中包含了很多內建的方法:
這裡通過lower實現
在views檢視函式中寫如下程式碼:
def tp3(request):
name= "ABCDEFG"
return render(request,"tp3.html",{"name":name})
在urls路由關係中新增如下:
path('tp3/', views.tp3),
在tp3頁面中寫如下:
{{ name }}
{{ name|lower }}
最後效果如下:
自定義方法
使用simple_tag的方法:
1、 在app下建立templatetags目錄
2、 建立py檔案
3、 建立template物件register
4、 @register.simple_tag
def func()
如果函式有引數:
def func(a1,a2)
5、 在settings配置檔案註冊app
6、 在頁面檔案頂部{%load py檔案%},如果存在繼承,這個要放在繼承下面
7、 最後在頁面使用的時候{% func %},如果有引數
{%func 2 3 %}
這裡有幾個問題需要注意:
1、 在app下建立templatetags目錄必須為templatetags不能更改
2、 建立py檔案的時候名字可以隨便定義
3、 在py檔案中必須寫如下程式碼:
from django import template from django.utils.safestring import mark_safe register = template.Library() @register.simple_tag
這四行程式碼必須有,並且的template物件register名字不能更改
按照上面的規則,在app下建立templatetags目錄
然後建立一個test.py檔案,程式碼如下:
並在setting中新增:
tp3.html中程式碼如下(注意高亮部分):
{% load test %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ name }}
{{ name|lower }}
{% func %}
</body>
</html>
最後效果如下:
我們將test中的func改成傳遞引數的,如下所示:
def func(a1,a2):
return a1+a2
在tp3.html中傳遞引數:
{% func 5 3 %}
結果如下:
自定義filter
filter和simple_tag的方法基本一樣,只需要做如下修改:
將test.py檔案中@register.simple_tag替換為@register.filter
在頁面中程式碼增加:
{{ 'hello'|testf:"gavin" }}
@register.filter
def testf(v1, v2):
return v1+":"+v2
這樣最終在頁面的效果如下:
而這裡對比filter和simple_tag,我們可以發現各有優缺點
1、其中fileter可以放在模板語言中的if條件中,而simple_tag則不能如:
{% if 'hello'|testf:"gavin" %}
{% endif %}
2、filter引數固定,simple_tag引數任意
分頁
在前端防止因為xss而現實字串的可以通{{ page_str|save}}
後端:可以通過匯入from django.utils.safestring import mark_safe,然後page_str = mark_safe(page_str)
通過下面例子用於理解分頁
這裡將分頁的功能封裝了一個類,改類內容如下:
from django.utils.safestring import mark_safe
class Page:
def __init__(self,current_page,data_count,per_page_count=10,page_num = 7):
'''
:param current_page: 當前頁
:param data_count: 資料的總數目
:param per_page_count: 每頁顯示的數目
:param page_num: 顯示幾頁內容
'''
self.current_page = current_page
self.data_count = data_count
self.per_page_count=per_page_count
self.page_num = page_num
@property
def start(self):
'''
:return: 返回得到起始
'''
return (self.current_page-1)*self.per_page_count
@property
def end(self):
'''
:return: 返回結束
'''
return self.current_page*self.per_page_count
@property
def total_count(self):
'''
:return: 返回總頁數
'''
v, y = divmod(self.data_count, self.per_page_count)
if y:
v += 1
return v
def page_str(self,base_url):
'''
:param base_url: 這裡是用於自定義url字首
:return: 返回的為頁面下端要顯示的跳轉頁的html語言的字串
'''
page_list = []
if self.total_count < self.page_num:
start_index = 1
end_index = self.total_count + 1
else:
if self.current_page <= (self.page_num + 1) / 2:
start_index = 1
end_index = self.page_num + 1
else:
start_index = self.current_page - (self.page_num - 1) / 2
end_index = self.current_page + (self.page_num + 1) / 2
if self.current_page + (self.page_num + 1) / 2 > self.total_count:
end_index = self.total_count + 1
start_index = self.total_count - self.page_num + 1
if self.current_page == 1:
prev = '<a class="page" href="#">上一頁</a>'
else:
prev = '<a class="page" href="%s?p=%s">上一頁</a>' % (base_url,self.current_page - 1)
page_list.append(prev)
for i in range(int(start_index), int(end_index)):
if i == self.current_page:
temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url,i, i)
else:
temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url,i, i)
page_list.append(temp)
if self.current_page == self.total_count:
nex = '<a class="page" href="#">下一頁</a>'
else:
nex = '<a class="page" href="%s?p=%s">下一頁</a>' % (base_url,self.current_page + 1)
page_list.append(nex)
go_page = """
<input type='text' /><a onclick="jumpTo(this,'%s?p=');">跳轉</a>
<script>
function jumpTo(ths,base){
var val = ths.previousSibling.value;
location.href = base + val;
}
</script>
""" %(base_url)
page_list.append(go_page)
page_str = "".join(page_list)
page_str = mark_safe(page_str)
return page_str
在views函式中呼叫:
from utils import pagination
def user_list(request):
current_page = request.GET.get("p",1)
current_page = int(current_page)
page_obj = pagination.Page(current_page,len(LI))
data = LI[page_obj.start:page_obj.end]
page_str = page_obj.page_str("/user_list/")
return render(request,"user_list.html",{"data":data,"page_str":page_str})
最終的效果如下:
cookie
客戶端瀏覽器上的一個檔案
以字典的方式存在
通常很多網站登入之後,網站的右上角會顯示當前使用者的使用者名稱,實現例子如下:
views裡寫如下程式碼:
def login(request):
print(request.method)
if request.method=="GET":
return render(request,"login.html")
if request.method =="POST":
u = request.POST.get("username")
p = request.POST.get("pwd")
dic = user_info.get(u)
print(u,p)
if not dic:
return render(request,"login.html")
if dic["pwd"] == p:
res = redirect("/index")
res.set_cookie('username1',u)
return res
else:
return render(request, "login.html")
def index(request):
#獲取當前登入的使用者名稱
v = request.COOKIES.get("username1")
if not v:
return redirect("/login")
return render(request,"index.html",{"current_user":v})
這樣使用者如果沒有登入的情況下就不能直接訪問index頁面了
,並且如果使用者登入之後也能顯示當前使用者的使用者名稱,也就是實現了使用者認證
設定Cookie:
res.set_cookie(key,value)
引數:
key 鍵
value=‘’ 值
max_age=None 超時時間,以秒作為單位。預設是關閉瀏覽器失效
expires=None 超時時間,這個是可以設定datatime
path="/" Cookie生效的路徑
domain=None Cookie生效的域名
secure=False https傳輸
httponly=False 只能http協議傳輸,無法被JavaScript獲取
分頁改造(結合cookie)
user_list.html程式碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pagination .page{
display: inline-block;
padding: 5px;
background-color: cyan;
margin: 5px;
}
.pagination .active{
background-color: blue;
color: white;
}
</style>
</head>
<body>
<ul>
{% for item in data %}
{% include 'li.html' %}
{% endfor %}
</ul>
<div>
<select id="ps" onchange="changePageSize(this)">
<option value="10">10</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="40">40</option>
</select>
</div>
<div class="pagination">
{{ page_str }}
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
$(function () {
var v = $.cookie("per_page_count");
$("#ps").val(v);
});
function changePageSize(ths) {
var v = $(ths).val();
console.log(v);
$.cookie("per_page_count",v);
location.reload()
}
</script>
</body>
</html>
頁面效果如上,實現的功能是當通過下拉框選擇不同的選項時,即每頁顯示的數量,這裡利用了jquery的cookie,jquery.cookie.js
關於cookie的加密
前文中我們通過
res.set_cookie('username1',u)
設定cookie,其實還有一種加密的方式,即:
res.set_signed_cookie("username1",u,salt="jiami")
通過salt這個引數實現加密,同樣的獲取cookie的時候也需要加上salt引數才能進行解密
使用者認證裝飾器
FBV的裝飾器用法
我們前面程式碼中:
def index(request):
#獲取當前登入的使用者名稱
v = request.COOKIES.get("username1")
if not v:
return redirect("/login")
return render(request,"index.html",{"current_user":v})
如果我們有多頁面都需要判斷使用者是否登入,如果每個都這樣寫就需要寫很多遍,所以這裡我們可以通過裝飾器實現
將上述程式碼進行更改:
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login")
return func(request,*args,**kwargs)
return inner
@auth
def index(request):
#獲取當前登入的使用者名稱
v = request.COOKIES.get("username1")
return render(request,"index.html",{"current_user":v})
CBV的裝飾器用法
下面是一個普通的CBV用法例子:
from django import views
class Order(views.View):
def get(self,request):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
如果我們只對get請求做認證
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login")
return func(request,*args,**kwargs)
return inner
from django import views
from django.utils.decorators import method_decorator
class Order(views.View):
@method_decorator(auth)
def get(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
這樣當訪問order的時候就加上了驗證功能
但是這樣是隻給get方法加驗證,如果想要給更多得方法加驗證的時候,通過下面方法實現:
def auth(func):
def inner(request,*args,**kwargs):
v = request.COOKIES.get("username1")
if not v:
return redirect("/login")
return func(request,*args,**kwargs)
return inner
from django import views
from django.utils.decorators import method_decorator
class Order(views.View):
@method_decorator(auth)
def dispatch(self, request, *args, **kwargs):
return super(Order,self).dispatch(request, *args, **kwargs)
def get(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
因為CBV每次需要先執行一個dispatch方法,我們在dispatch方法上加認證,這樣就相當於在所有的上面加上了認證
但是這種方法有人會覺得多寫了一個dispatch方法,可以將其簡化為:
@method_decorator(auth,name="dispatch")
class Order(views.View):
def get(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
def post(self,request):
v = request.COOKIES.get("username1")
return render(request, "index.html", {"current_user": v})
session的工作過程
1、 生成隨機字串
2、 寫到使用者瀏覽器的cookie中
3、 儲存到session中
4、 在隨機字串對應的字典中設定相關內容
而上述過程在Django中的體現為:
request.session["username"]=user
這裡的username為通過request.POST.get("username")從前端html頁面中獲取到的使用者名稱資訊
注意:
在Django中要用session中一定要先執行:
python manage.py makemigrations
python manage.py migrate
當用戶登入的時候的就會在資料庫的django_session表中記錄session資訊
同樣的通過request.session["username"]也可以獲取相應的值
在這個過程中:
1、 首先獲取當前使用者的隨機字串
2、 根據隨機字串獲取對應的內容
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失效策略。
配置setting.py
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),此處別名依賴快取的設定
3.如果是檔案session, 需要在settings.py中配置如下:
SESSION_ENGINE = 'django.contrib.sessions.backends.file' (引擎)
SESSION_FILE_PATH=None 快取檔案路徑,如果為None,則使用tempfile模組獲取一個臨時地址tempfile.gettempdir()
4. 如果是快取+資料庫session,需要在settings.py中配置如下:
SESSION_ENGINE='django.contrib.sessions.backends.cached_db' (引擎)