Python自動化開發學習22-Django下(Form)
這裏不是驗證用戶名密碼是否正確,這部分內容之前已經講過了。這裏要驗證的是數據格式,這步驗證是在收到請求後先執行的驗證。只有數據格式驗證通過,才會驗證用戶名密碼是否正確。如果數據格式驗證不通過,則返回錯誤信息。
講師的博客地址:http://www.cnblogs.com/wupeiqi/articles/6144178.html
測試環境
先寫一個form表單,host.html:
<form action="/host/" method="POST"> {% csrf_token %} <p><input type="text" name="hostname" placeholder="主機名"></p> <p><input type="text" name="ip" placeholder="IP地址"></p> <p><input type="text" name="port" placeholder="端口號"></p> <p><input type="text" name="email" placeholder="E-Mail"></p> <p><input type="submit" value="登錄"></p> </form>
然後導入驗證的模塊寫一個類:
from django import forms
class FM(forms.Form):
# 變量的字段名匹配form表單裏的name屬性的值,必須一樣
hostname = forms.CharField()
ip = forms.GenericIPAddressField(protocol=‘ipv4‘)
port = forms.IntegerField()
email = forms.EmailField()
# 上面只有這麽幾個,如果提交的數據有很多,那麽其他數據都不收(丟棄)
然後是在處理函數裏,通過這個類進行驗證:
def host(request): if request.method == ‘GET‘: return render(request, ‘host.html‘) if request.method == ‘POST‘: obj = FM(request.POST) # 實例化,把POST的數據傳入 res = obj.is_valid() # 獲取結果 print(res) # 驗證通過是True,不通過則是False if res: print(obj.cleaned_data) # 這是一個原生的字典,裏面就是提交來的數據 return HttpResponse(‘OK‘) else: print(obj.errors) # 這裏是一串html的列表的代碼 print(type(str(obj.errors))) # 這裏的str方法居然是django提供的,變成(<class ‘django.utils.safestring.SafeText‘>) print(obj.errors.as_json()) # 也可以拿到JSON的格式 return HttpResponse(str(obj.errors)) # 通過str方法後,頁面上會直接按html代碼處理
現在可以打開頁面測試效果。
驗證通過,obj.cleaned_data 裏就是合法的數據的字典,可以進行後續的操作。
驗證不通過,obj.errors是錯誤信息(html的格式,一個ul無序列表),也可以通過 obj.errors.as_json() 獲取一個JSON格式的錯誤信息:
{"hostname": [{"message": "This field is required.", "code": "required"}],
"ip": [{"message": "Enter a valid IPv4 address.", "code": "invalid"}],
"port": [{"message": "Enter a whole number.", "code": "invalid"}],
"email": [{"message": "Enter a valid email address.", "code": "invalid"}]}
錯誤信息包括,錯誤類型(code)和錯誤信息(message),這裏的錯誤信息也可以自定義,我要中文的。
自定義錯誤信息
通過參數error_messages設置自定義的錯誤信息,code的值就是key,然後把你希望的內容填在value裏:
from django import forms
class FM(forms.Form):
# 變量的字段名匹配form表單裏的name屬性的值,必須一樣
hostname = forms.CharField(
max_length=12,
min_length=6,
error_messages={‘required‘: "設備名不能為空",
‘max_length‘: "設備名太長,不能超過12",
‘min_length‘: "設備名太短,不能小於6"})
ip = forms.GenericIPAddressField(protocol=‘ipv4‘)
port = forms.IntegerField()
email = forms.EmailField(error_messages={‘required‘: "郵箱不能為空", ‘invalid‘: "郵箱格式錯誤"})
返回錯誤信息到表單頁面
現在使用render方法,把錯誤信息返回給頁面。所有內容都在obj裏,把整個obj返回:
return render(request, ‘host.html‘, {‘obj‘: obj})
通過模板語言獲取錯誤信息。這裏註意最後要在.0拿到的才是錯誤信息的內容
<form action="/host/" method="POST">
{% csrf_token %}
<p><input type="text" name="hostname" placeholder="主機名">{{ obj.errors.hostname.0 }}</p>
<p><input type="text" name="ip" placeholder="IP地址">{{ obj.errors.ip.0 }}</p>
<p><input type="text" name="port" placeholder="端口號">{{ obj.errors.port.0 }}</p>
<p><input type="text" name="email" placeholder="E-Mail">{{ obj.errors.email.0 }}</p>
<p><input type="submit" value="登錄"></p>
</form>
還有一個問題是,你一點提交你之前在input裏填的內容會被清空。這樣不好。
自動生成html標簽(保留上次輸入的信息)
上面講的都是form組件的一個功能,其實django的form組件主要是完成下面的2個功能的
form組件的2大功能:
- 驗證(顯示錯誤信息)
- 保留用戶上次輸入的信息,通過自動生成的html標簽實現
自動生成input標簽
用下面的方法可以自動生成input標簽。form標簽這裏加上了一個novalidate屬性,是為了禁用客戶端的表單驗證功能,可以直接看到服務端返回的驗證信息。換句話說,就是自動生成的標簽還幫我麽加上了簡單的客戶端的初步驗證的功能:
<form novalidate action="/host/" method="POST">
{% csrf_token %}
<p>{{ obj.hostname.label_tag }}{{ obj.hostname }}{{ obj.errors.hostname.0 }}</p>
<p>{{ obj.ip.label_tag }}{{ obj.ip }}{{ obj.errors.ip.0 }}</p>
<p>{{ obj.port.label_tag }}{{ obj.port }}{{ obj.errors.port.0 }}</p>
<p>{{ obj.email.label_tag }}{{ obj.email }}{{ obj.errors.email.0 }}</p>
<p><input type="submit" value="登錄"></p>
</form>
上面只是生成了input標簽,裏面沒有設置placeholder,如果需要加上自定義屬性後面會講。
這裏還有這些常用的變量:
- {{ obj.hostname.label_tag }} :自動生成標簽,裏面是這個樣子的
<label for="id_hostname">Hostname:</label>
,一個for一個value,可以用下面2個變量單獨拿到這2個值。 - {{ obj.hostname.label }} :標簽的value值
- {{ obj.hostname.id_for_label }} :input標簽的id,就是label標簽裏for的值,有這個可以自己寫label標簽了
- {{ obj.hostname.errors }} :錯誤信息,貌似和{{ obj.errors.hostname.0 }}是一樣的,只是存在不同的位置
這裏處理函數要註意,因為GET請求也需要返回obj對象(但是GET裏不用填參數)給頁面了,所以要做如下的修改:
def host(request):
if request.method == ‘GET‘:
obj = FM() # 這裏也需要創建一個對象,因為需要它生成標簽,但是不需要傳參數
return render(request, ‘host.html‘, {‘obj‘: obj})
if request.method == ‘POST‘:
obj = FM(request.POST) # 實例化,把POST的數據傳入
res = obj.is_valid() # 獲取結果
if res:
print(obj.cleaned_data)
return HttpResponse(‘OK‘)
else:
# return HttpResponse(str(obj.errors))
return render(request, ‘host.html‘, {‘obj‘: obj})
自動生成表單
還是推薦用上面的,這個可定制性太差了。
form標簽自己寫,submit自己寫,其他的都不用寫。有3中方式:
- {{ obj.as_p }} :p標簽。
- {{ obj.as_ul }} :生成的是li標簽,所以外面應該再包一層ul標簽?
- {{ obj.as_table }} :生成table的tbody標簽,所以外面得自己再包一層table標簽。
<form novalidate action="/host/" method="POST">
{% csrf_token %}
{{ obj.as_p }}
<p><input type="submit" value="登錄"></p>
</form>
</form><form novalidate action="/host/" method="POST">
{% csrf_token %}
{{ obj.as_ul }}
<p><input type="submit" value="登錄"></p>
</form>
</form>
<form novalidate action="/host/" method="POST">
{% csrf_token %}
<table>
{{ obj.as_table }}
</table>
<p><input type="submit" value="登錄"></p>
</form>
HTML插件(widgets)
繼續看form組件的2大功能:
- 驗證(顯示錯誤信息),forms.CharField,負責驗證
- 保留用戶上次輸入的信息,forms.CharField內部包含一個插件widget,負責生成html標簽。設了公有屬性,成員屬性默認參數是None,所以沒定義都是取默認的公有屬性
插件可以定義標簽的類型,默認是文本框,可以改變成多行文本、單選復選,總之是所有的input標簽的類型。
插件還可以定義標簽的屬性,這樣就實現了自定義樣式。
定義前先要導入插件的模塊,from django.forms import widgets
from django.forms import Form # 這個是要繼承的類
from django.forms import fields # 學到這裏,字段導入這個模塊
from django.forms import widgets # 這個模塊是插件
class FM(forms.Form):
# 變量的字段名匹配form表單裏的name屬性的值,必須一樣
hostname = fields.CharField(
max_length=12,
min_length=6,
error_messages={‘required‘: "設備名不能為空",
‘max_length‘: "設備名太長,不能超過12",
‘min_length‘: "設備名太短,不能小於6"})
ip = fields.GenericIPAddressField(
widget=widgets.Textarea, # 這裏改成多行文本,不定義樣式後面就不用跟括號
protocol=‘ipv4‘)
port = fields.IntegerField(
widget=widgets.TextInput(attrs={‘class‘: ‘c1‘}) # 後面跟括號,寫上你要自定義的屬性
)
email = fields.EmailField(error_messages={‘required‘: "郵箱不能為空", ‘invalid‘: "郵箱格式錯誤"})
學到這裏,之前導入模塊的方式不太好,明確導入需要的模塊。
導入 from django.forms import Form
模塊,這個是要繼承的類
導入 from django.forms import fields
模塊來定義字段,這個 fields 是所有字段類型的基類。開始用的是 forms.CharField
都改成 fields.CharField
。
導入 from django.forms import widgets
模塊來定義插件。如果只定義類型,後面就不用加括號。如果需要自定義屬性,就在後面加括號,attrs裏以字典的形式寫上各種自定義的屬性。
密碼的input框類型,可以用這個插件 widget=widgets.PasswordInput,
。
所有的驗證類型
查看文件 django\forms\fields.py ,裏面有這麽多種驗證類型。第一個Field是基類,後面的都是繼承這個Field的子類或者是孫子類:
__all__ = (
‘Field‘, ‘CharField‘, ‘IntegerField‘,
‘DateField‘, ‘TimeField‘, ‘DateTimeField‘, ‘DurationField‘,
‘RegexField‘, ‘EmailField‘, ‘FileField‘, ‘ImageField‘, ‘URLField‘,
‘BooleanField‘, ‘NullBooleanField‘, ‘ChoiceField‘, ‘MultipleChoiceField‘,
‘ComboField‘, ‘MultiValueField‘, ‘FloatField‘, ‘DecimalField‘,
‘SplitDateTimeField‘, ‘GenericIPAddressField‘, ‘FilePathField‘,
‘SlugField‘, ‘TypedChoiceField‘, ‘TypedMultipleChoiceField‘, ‘UUIDField‘,
)
內置字段
創建Form類時,主要涉及到【字段】和【插件】,字段用於對用戶請求數據的驗證,插件用於自動生成HTML。
Django內置字段如下:
- Field
- required=True,是否允許為空,默認都是必填的,有非必填項置為False
- widget=None,HTML插件
- label=None,用於生成Label標簽或顯示內容,自定義label標簽的value的內容,不包括最後的一個冒號
- label_suffix=None,Label內容後綴,自定義label標簽的後綴,應該是拼接到所有label後面生成頁面顯示的label的value的值,默認是英文的冒號
- initial=None,初始值,可以為標簽設置初始的內容
- help_text=‘‘,幫助信息(在標簽旁邊顯示)
- error_messages=None,自定義錯誤信息 {‘required‘: ‘不能為空‘, ‘invalid‘: ‘格式錯誤‘}
- show_hidden_initial=False,是否在當前插件後面再加一個隱藏的且具有默認值的插件(可用於檢驗兩次輸入是否一直)。就是在你的標簽的旁邊在生成一個隱藏的一模一樣的標簽,你可以比較用戶的標簽相對於之前的狀態是否變化了。
- validators=[],自定義驗證規則,這裏導入一個錯誤異常,比如RegexValidator。寫上自定義的正則表達式和錯誤信息,可以按自定義的規則進行驗證。這是個列表,可以定義多個規則,後面有例子。
- localize=False,是否支持本地化。比如默認的時間都是UTC時間,設置了本地化,就直接顯示的是本地的時間了
- disabled=False, 是否可以編輯。
- 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),自定義正則進行驗證。和在 fields.CharField 裏定義validators是一樣的。
- regex,自定制正則表達式
- max_length=None,最大長度
- min_length=None,最小長度
- error_message=None,忽略,錯誤信息使用 error_messages={‘invalid‘: ‘...‘}
- EmailField(CharField)
- FileField(Field)
- allow_empty_file=False,是否允許空文件
- 使用時註意1:form表單中 enctype="multipart/form-data"
- 使用時註意2:view函數中 obj = MyForm(request.POST, request.FILES)
- ImageField(FileField)
- 需要PIL模塊,pip3 install Pillow
- 使用時的註意點同FileField
- URLField(Field)
- BooleanField(Field)
- NullBooleanField(BooleanField)
- ChoiceField(Field),單選返回值為字符串,多選返回值為列表
- choices=(),選項,如:choices = ((0,‘上海‘),(1,‘北京‘),)
- required=True,是否必填
- widget=None,插件,默認
widget=widgets.Select
,還可以SelectMultiple(復選select),widgets.RadioSelect(單選),CheckboxSelectMultiple(多選) - 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= "",空值的默認值
- ComboField(Field)
- fields=(),使用多個驗證。如下,驗證最大長度20,又驗證郵箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
- MultiValueField(Field)
- 抽象類,只能被繼承。子類中可以實現聚合多個字典去匹配一個值,要配合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。protocol必須為both才能啟用
- SlugField(CharField),數字,字母,下劃線,減號(連字符)
- UUIDField(CharField),uuid類型
內置字段的一些例子
Field 屬性 validators,自定義驗證規則
from django.forms import Form
from django.core.validators import RegexValidator
class MyForm(Form):
mobile = fields.CharField(
validators=[RegexValidator(r‘^[0-9]+$‘, ‘請輸入數字‘), RegexValidator(r‘^159[0-9]+$‘, ‘數字必須以159開頭‘)],
)
FilePathField(ChoiceField)
把你把一個文件夾下的所有的文件列舉出來,然後選一個提交:
from django.forms import Form
from django.forms import fields
class FM(Form):
folder = fields.FilePathField(path=‘app01‘)
def folder(request):
obj = FM()
return render(request, ‘folder.html‘, {‘obj‘: obj})
模板語言會自動生成下拉列表:
{{ obj.folder }}
頁面上會生成一個下拉框,裏面顯示的是文件名,後面有個提交按鈕。提交的是文件路徑。
內置插件
下面這些都是內置的插件。插件後面都可以加上 (attrs={‘key‘: ‘value‘})
進行自定制屬性:
- 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
初始化數據
在Web應用程序中開發編寫功能時,時常用到獲取數據庫中的數據並將值初始化在HTML中的標簽上。
獲取數據後把數據放在一個字典裏。如果是數據庫查詢,ORM可以直接獲取字典形式的數據。
之前GET請求裏用的是 obj = FM()
,相當於傳入空值,現在可以把字典作為參數傳入 obj = FM(initial=dict)
。這樣頁面上使用Form生成的html標簽裏就有字典裏的值了。
序列化操作(待補充)
上面都是用form表單來提交的。之前還學過用Ajax提交。提交沒有問題,但是Ajax提交返回的要求是字符串。或者是字典、列表可以用JSON序列化為字符串。但是這裏返回的是 obj = FM()
對象,如何序列化成字符串返回給Ajax?
Python自動化開發學習22-Django下(Form)