Python自动化开发学习22-Django下(Form)
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,不通過則是Falseif 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格式的錯誤信息:
錯誤信息包括,錯誤類型(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屬性,是為了禁用客戶端的表單驗證功能,可以直接看到服務端返回的驗證信息。換句話說,就是自動生成的標簽還幫我么加上了簡單的客戶端的初步驗證的功能:
上面只是生成了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標簽。
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 模塊來定義字段,這個 fields 是所有字段類型的基類。開始用的是 forms.CharField 都改成 fields.CharField。
導入 from django.forms import widgets 模塊來定義插件。如果只定義類型,后面就不用加括號。如果需要自定義屬性,就在后面加括號,attrs里以字典的形式寫上各種自定義的屬性。
密碼的input框類型,可以用這個插件 widget=widgets.PasswordInput, 。
所有的驗證類型
查看文件 django\forms\fields.py ,里面有這么多種驗證類型。第一個Field是基類,后面的都是繼承這個Field的子類或者是孫子類:
內置字段
創建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)
把你把一個文件夾下的所有的文件列舉出來,然后選一個提交:
模板語言會自動生成下拉列表:
{{ 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=dic) 。這樣頁面上使用Form生成的html標簽里就有字典里的值了。
直接修改之前的例子,先準備好一個有數據的字典(這里就不查數據庫了)。然后只需要傳入這個字典就好了,別的都不用改。這樣生成的頁面里的輸入框是會把字典里的值填上的:
直接像這樣 obj = FM(dic) 傳字典也是可以的,或者這樣 obj = FM({'hostname': "HOST1", 'ip': '192.168.2.1',}) 。至少效果是一樣的。這里看著按位置參數傳遞的話,參數不是傳給initial的。
序列化操作(待補充)
上面都是用form表單來提交的。之前還學過用Ajax提交。提交沒有問題,但是Ajax提交返回的要求是字符串。或者是字典、列表可以用JSON序列化為字符串。但是這里返回的是 obj = FM() 對象,如何序列化成字符串返回給Ajax?
轉載于:https://blog.51cto.com/steed/2104396
總結
以上是生活随笔為你收集整理的Python自动化开发学习22-Django下(Form)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lol小小英雄稀有蛋能开出什么 英雄联盟
- 下一篇: Python中的装饰器,迭代器,生成器