Django(七)快取、訊號、Form
大綱
一、快取
1.1、五種快取配置
1.2配置
2.1、三種應用(全域性、檢視函式、模板)
2.2 應用多個快取時生效的優先順序
二、訊號
1、Django內建訊號
2、自定義訊號
三、Form
1、初始form,建立form
2、自定製form錯誤資訊,前端顯示
3、保留上一次提交的資料,自動生成html標籤
4、更簡潔的html標籤生成方法
5、自定義樣式
6、form 內建欄位
常用選擇外掛
7、初始化操作
一、快取
除了Django這個web框架之外、其他框架都沒有快取。Django的配置一下就可以使用。
1.1、五種快取配置
Django中提供了5種快取方式:
- 開發除錯(快取哪裡都不放,只都配置好,測試用)
- 記憶體
- 檔案
- 資料庫
- Memcache快取
- (使用 python-memcached模組 連線memcache)
- (使用 pylibmc模組 連線memcache)
1.2配置
- 開發除錯
# 此為開始除錯用,實際內部不做任何操作
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
'TIMEOUT' : 300, # 快取超時時間(預設300,None表示永不過期,0表示立即過期)
'OPTIONS':{
'MAX_ENTRIES': 300, # 最大快取個數(預設300)
'CULL_FREQUENCY': 3, # 快取到達最大個數之後,剔除快取個數的比例,即:1/CULL_FREQUENCY(預設剔除1/3)
},
'KEY_PREFIX': '', # 快取key的字首(預設空)
'VERSION' : 1, # 快取key的版本(預設1)
'KEY_FUNCTION' 函式名 # 生成key的函式(預設函式會生成為:【字首:版本:key】)
}
}
# 自定義key 名
def default_key_func(key, key_prefix, version):
"""
Default function to generate keys.
Constructs the key used by all other methods. By default it prepends
the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
function with custom key making behavior.
"""
return '%s:%s:%s' % (key_prefix, version, key)
def get_key_func(key_func):
"""
Function to decide which key function to use.
Defaults to ``default_key_func``.
"""
if key_func is not None:
if callable(key_func):
return key_func
else:
return import_string(key_func)
return default_key_func
- 記憶體
# 此快取將內容儲存至記憶體的變數中
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
# 注:其他配置同開發除錯版本
- 檔案
# 此快取將內容儲存至檔案
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
# 'LOCATION': os.path.join(BASE_DIR,'cache'),
}
}
# 注:其他配置同開發除錯版本
- 資料庫
# 此快取將內容儲存至資料庫
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table', # 資料庫表
}
}
# 注:執行建立表命令 python manage.py createcachetable
- Memcache快取(python-memcached模組)
# 此快取使用python-memcached模組連線memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = { # 連線本地cookie
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
# 可以配置多個,連線多個memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
# ('172.19.26.240:11211',10) 調權重
# ('172.19.26.242:11211',11) 調權重,memcache模組實現的
]
}
}
- Memcache快取(pylibmc模組)
# 此快取使用pylibmc模組連線memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
2.1、三種應用(全域性、檢視函式、模板)
快取配置示例(檔案方式)
settings.py
# 這裡在檔案末尾處新增。並新建資料夾 cache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': os.path.join(BASE_DIR,'cache'),
}
}
urls.py url(r'^cache/$', views.cache),
- 單獨檢視快取 (views函式快取)*
# 方式一:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 15分鐘
def my_view(request):
...
# 方式二:
from django.views.decorators.cache import cache_page
urlpatterns = [
url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]
- 區域性檢視使用 (模板區域性快取)
# a. 引入TemplateTag
{% load cache %}
# b. 使用快取
{% cache 5000 快取key %}
快取內容
{% endcache %}
示例:
views.py
from django.views.decorators.cache import cache_page
# @cache_page(10) views函式快取
def cache(request):
import time
ctime = time.time()
return render(request, 'cache.html', {'ctime': ctime})
cache.html
{% load cache %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>{{ ctime }}</h1>
<h1>{{ ctime }}</h1>
{% cache 10 c1 %} <!--模板區域性快取-->
<h1>{{ ctime }}</h1>
{% endcache %}
</body>
</html>
- 全站使用
例如 部落格等快取,通過中介軟體實現全站快取。
加快取中介軟體,那麼多中介軟體加在什麼位置?
請求時:快取加在中介軟體裡的最後一個,比如一次經過1、2、3、4中介軟體,加在4
返回事:快取加在中介軟體裡的第一個,如上返回依次經過4、3、2、1,加在1
django 中,匯入模組,可以實現。
# 使用中介軟體,經過一系列的認證等操作,
# 如果內容在快取中存在,則使用FetchFromCacheMiddleware獲取內容並返回給使用者,
# 當返回給使用者之前,判斷快取中是否已經存在,
# 如果不存在則UpdateCacheMiddleware會將快取儲存至快取,從而實現全站快取
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware', # 只有process_response
# 其他中介軟體...
'django.middleware.cache.FetchFromCacheMiddleware', # 只有process_view
]
CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = ""
CACHE_MIDDLEWARE_KEY_PREFIX = ""
2.2 應用多個快取時生效的優先順序
另外:session引擎快取配置,就是放在這裡的快取裡。
如果同時應用了兩個級別的快取,比如views快取和模板區域性快取,哪個生效?
哪個生效,和請求的宣告週期的有關係的
所以最先生效的是全站、之後views、最後模板區域性快取。
二、訊號
Django中提供了“訊號排程”,用於在框架執行操作時解耦。通俗來講,就是一些動作發生的時候,訊號允許特定的傳送者去提醒一些接受者。
比如在資料庫操作,插入一條資料之前和之後都寫入日誌。
這裡裝飾器就實現不了了,裝飾器用在函式上,這裡可能在一條程式碼前後,而且是每次。
Django是非常牛逼的框架,在很多地方都放置了鉤子。我們呼叫鉤子就可以了。
我們可以在訊號裡面註冊很多個函式。觸發訊號時,會把訊號裡的函式執行一遍。
1、Django內建訊號
# Model signals
/ pre_init # django的modal執行其構造方法前,自動觸發
\ post_init # django的modal執行其構造方法後,自動觸發
/ pre_save # django的modal物件儲存前,自動觸發
\ post_save # django的modal物件儲存後,自動觸發
/ pre_delete # django的modal物件刪除前,自動觸發
\ post_delete # django的modal物件刪除後,自動觸發
m2m_changed # django的modal中使用m2m欄位操作第三張表(add,remove,clear)前後,自動觸發
class_prepared # 程式啟動時,檢測已註冊的app中modal類,對於每一個類,自動觸發
# Management signals
pre_migrate # 執行migrate命令前,自動觸發
post_migrate # 執行migrate命令後,自動觸發
# Request/response signals
request_started # 請求到來前,自動觸發
request_finished # 請求結束後,自動觸發
got_request_exception # 請求異常後,自動觸發
# Test signals
setting_changed # 使用test測試修改配置檔案時,自動觸發
template_rendered # 使用test測試渲染模板時,自動觸發
# Database Wrappers
connection_created # 建立資料庫連線時,自動觸發
對於Django內建的訊號,僅需註冊指定訊號,當程式執行相應操作時,自動觸發註冊函式:
from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created
def func1(sender, **kwargs):
print("xxoo_callback")
# print(sender,kwargs) 兩個引數會把內容傳遞給訊號
xxoo.connect(func1) # 訊號註冊函式func1。xxoo指上述匯入的訊號
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print("Request finished!")
如上的程式碼檔案,如何被呼叫執行呢?
可以在project下的同名目錄下__init__.py
裡匯入這個檔案,這樣一執行就自動註冊了
2、自定義訊號
- 定義訊號
import django.dispatch
sg_name = django.dispatch.Signal(providing_args=["toppings", "size"]) # 觸發訊號至少要傳兩個引數
- 註冊訊號
def callback(sender, **kwargs):
print("callback")
print(sender,kwargs)
sg_name.connect(callback)
- 觸發訊號
from 路徑 import sg_name
sg_name.send(sender='傳送者隨便填',toppings=123, size=456)
由於內建訊號的觸發者已經整合到Django中,所以其會自動呼叫,而對於自定義訊號則需要開發者在任意位置觸發。
比如對系統狀態閥值設定,到達某個狀態,觸發訊號。
這樣只註冊訊號就可以,類似插拔式,降低程式耦合。
三、Form
django中的Form功能操作:
驗證使用者請求
生成HTML標籤
(保留上一次提交的資料)
比如使用者註冊資訊驗證:使用者名稱不允許為空、密碼最短6位,如果自己寫需要些很多,比較麻煩,而Django form提供了特別便捷的實現方式
Form表單驗證模板模板是一個類,建立如下:
1、初始form,建立form
views.py
from django import forms
class FM(forms.Form):
# 只關心處理自己定義的form表單資料,惡意攻擊定義的資料不處理
user = forms.CharField()
pwd = forms.CharField() # 這裡的變數名必須和html form裡的name保持一致
email = forms.EmailField()
def fm(request):
if request.method == "GET":
return render(request, "fm.html")
elif request.method == "POST":
# 獲取使用者所有資料,每條資料請求的驗證
# 成功 --> 獲取所有的正確資訊;失敗 --> 顯示錯誤資訊
obj = FM(request.POST)
r1 = obj.is_valid()
if r1: # 返回的正確資訊
print(obj.cleaned_data)
else: # 返回的錯誤資訊
print(obj.errors)
print(obj.errors.as_json())
return redirect('/fm/')
fm.html
<body>
<form action="/fm/" method="POST">
{% csrf_token %}
<input type="text" name="user">
<input type="text" name="pwd">
<input type="text" name="email">
<input type="submit" value="提交" />
</form>
</body>
錯誤提示如下:
<ul class="errorlist"><li>email<ul class="errorlist"><li>This field is required.</li></ul></li><li>pwd<ul class="errorlist"><li>This field is required.</li></ul></li><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
{"email": [{"message": "This field is required.", "code": "required"}], "pwd": [{"message": "This field is required.", "code": "required"}], "user": [{"message": "This field is required.", "code": "required"}]}
上面的錯誤資訊為英文,能不能自定製呢?
2、自定製form錯誤資訊,前端顯示
views.py
from django import forms
class FM(forms.Form):
# 只關心處理自己定義的form表單資料,惡意攻擊定義的資料不處理
user = forms.CharField(error_messages={'required':'使用者名稱不能為空'})
pwd = forms.CharField(
max_length=12,
min_length=6,
error_messages={'required':'密碼不能為空','min_length':'密碼長度不能小於6','max_length':'密碼長度不能大於12'}
)
email = forms.EmailField(error_messages={'required':'使用者名稱不能為空','invalid':'郵箱格式錯誤'})
def fm(request):
if request.method == "GET":
return render(request, "fm.html")
elif request.method == "POST":
obj = FM(request.POST)
r1 = obj.is_valid()
if r1: # 返回的正確資訊
print(obj.cleaned_data)
else: # 返回的錯誤資訊
# ErrorDict
# print(obj.errors['user'][0])
# print(obj.errors.as_json())
return render(request,'fm.html',{'obj':obj})
return render(request,'fm.html')
fm.html
<form action="/fm/" method="POST">
{% csrf_token %}
<p><input type="text" name="user"> {{ obj.errors.user.0 }}</p>
<p><input type="text" name="pwd"> {{ obj.errors.pwd.0 }}</p>
<p><input type="text" name="email"> {{ obj.errors.email.0 }}</p>
<input type="submit" value="提交" />
</form>
3、保留上一次提交的資料,自動生成html標籤
views裡面不僅能幫我們生成錯誤資訊,還能幫我們生成html標籤。
fm.html
<form action="/fm/" method="POST">
{% csrf_token %}
<p>{{ obj.user }} {{ obj.errors.user.0 }}</p>
<p>{{ obj.pwd }} {{ obj.errors.pwd.0 }}</p>
<p>{{ obj.email }} {{ obj.errors.email.0 }}</p>
<input type="submit" value="提交" />
</form>
views.py
def fm(request):
if request.method == "GET":
obj = FM() # 自動生成html時,get這裡也需要建立物件
return render(request, "fm.html",{'obj':obj})
elif request.method == "POST":
obj = FM(request.POST)
r1 = obj.is_valid()
if r1: # 返回的正確資訊
print(obj.cleaned_data) # 這是個字典,註冊直接下面那一句就成功了
# models.UserInfo.objects.create(**obj.cleaned_data)
else: # 返回的錯誤資訊
return render(request,'fm.html',{'obj':obj})
return render(request,'fm.html')
4、更簡潔的html標籤生成方法
這裡生成雖然方便,但是可定製化不如上面高。
- obj.as_p
- obj.as_ul
- obj.as_table
可以把上面html裡的form改為:
<form action="/fm/" method="POST">
{% csrf_token %}
{{ obj.as_p }}
<input type="submit" value="提交" />
</form>
<form action="/fm/" method="POST">
{% csrf_token %}
{{ obj.as_ul }}
<input type="submit" value="提交" />
</form>
<form action="/fm/" method="POST">
{% csrf_token %}
<table>
{{ obj.as_table }}
</table>
<input type="submit" value="提交" />
</form>
5、自定義樣式
form類裡面的欄位,只有一個功能,就是驗證客戶端發過來的資料。生成html的功能做不了。
但是怎麼生成的html標籤呢,在charfield裡面有個外掛,外掛生成的。在其原始碼裡做了html字串的拼接返回。
from django import forms
from django.forms import widgets # 外掛在這裡面
class FM(forms.Form):
# 欄位本身只做驗證
user = forms.CharField( # 修改html標籤,並指定樣式##############
error_messages={'required':'使用者名稱不能為空'},
widget=widgets.Textarea(attrs={'class':'c1'}), # 頁面再看就是textarea了
label="使用者名稱"
)
pwd = forms.CharField(
max_length=12,
min_length=6,
error_messages={'required':'密碼不能為空','min_length':'密碼長度不能小於6','max_length':'密碼長度不能大於12'},
widget=widgets.PasswordInput # 密碼密文顯示,如果自定義樣式也可加上(attrs……)
)
email = forms.EmailField(error_messages={'required':'使用者名稱不能為空','invalid':'郵箱格式錯誤'})
而欄位都在from django.forms import fields
裡面,所以上面的forms可以改用fields
email = fields.EmailField()
前端<p>{{ obj.user.label }}{{ obj.user }} {{ obj.errors.user.0 }}</p>
外掛裡面input、checkbox、select、redio等全部都有
6、form 內建欄位
Field
required=True, 是否允許為空
widget=None, HTML外掛
label=None, 用於生成Label標籤或顯示內容
initial=None, 初始值
help_text='', 幫助資訊(在標籤旁邊顯示)
error_messages=None, 錯誤資訊 {'required': '不能為空', 'invalid': '格式錯誤'}
* show_hidden_initial=False, 是否在當前外掛後面再加一個隱藏的且具有預設值的外掛(可用於檢驗兩次輸入是否一直)
* 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型別
...
說一下上面的ChoiceField,它主要做選項用的
city = fields.ChoiceField(
choices=[(0,'上海'),(1,'廣州')]
)
city2 = fields.MultipleChoiceField(
choices=[(0,'上海'),(1,'廣州')]
)
常用選擇外掛
# 單radio,值為字串
# user = fields.CharField(
# initial=2,
# widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
# 單radio,值為字串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.RadioSelect
# )
# 單select,值為字串
# user = fields.CharField(
# initial=2,
# widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
# 單select,值為字串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.Select
# )
# 多選select,值為列表
# user = fields.MultipleChoiceField(
# choices=((1,'上海'),(2,'北京'),),
# initial=[1,],
# widget=widgets.SelectMultiple
# )
# 單checkbox
# user = fields.CharField(
# widget=widgets.CheckboxInput()
# )
# 多選checkbox,值為列表
# user = fields.MultipleChoiceField(
# initial=[2, ],
# choices=((1, '上海'), (2, '北京'),),
# widget=widgets.CheckboxSelectMultiple
# )
Django內建外掛:
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
自定義
- 類
- 欄位(校驗)
- 外掛(生成HTML)
7、初始化操作
在Web應用程式中開發編寫功能時,時常用到獲取資料庫中的資料並將值初始化在HTML中的標籤上。
def fm(request):
if request.method == "GET":
dic = {
"user":'r1',
"pwd":'123456',
"email":'[email protected]',
"city":1,
"city2":[1,2],
}
obj = FM(initial=dic) # 初始化
return render(request, "fm.html",{'obj':obj})
# ……