Django框架-Form組件
一、DjangoForm組件介紹
我們之前在html頁面中利用form表單向後端提交數據時,都會寫一些獲取用戶輸入的標簽並且用form標簽把它們包起來。
與此同時我們在好多場景下都需要對用戶的輸入做校驗,比如驗證用戶是否輸入,輸入的長度和格式等是否正確,如果用戶輸入的內容有錯誤就需要在頁面上相應的位置顯示對應的錯誤信息。
Django Form組件就實現了上面所述的功能。
1、生成頁面可用的html標簽(生成input框)
2、對用戶提交的數據進行校驗
3、錯誤信息的展示
4、保留上次的輸入
缺點:只能生成form表單裏面的內容,form標簽需要自己建立,提交的按鈕需要自己寫,csrf_token也需要自己添加
二、form組件的字段和插件
創建form類時,主要涉及到【字段】和【插件】,字段用於對用戶請求數據的驗證,插件用於自動生成html
字段創建,默認為input框中的類型為text,可以通過插件來修改成按鈕,多選框等等。
想通過插件修改,需要導入插件模塊:from django.forms import widgets
1、字段
CharField(參數) 所有的字段都繼承Field類,初始化都是TextFied,改變type都可以通過widgets改變
註:所有的字段類型都可以用CharField,然後通過widgets來修改
ChoiceField() 選擇類的字段
通過widgets可改變為:widget = forms.widgets.radioSelect()
radioSelect(單選按鈕)
Select(單選下拉)
SelectMultiple(多選下拉)
checkbox(單選方形按鈕)
checkboxSelectMultiple(多選方形按鈕)
示例:如何使用choiseField字段,及使用數據庫中的數據
class LoginForm(forms.Form): ... hobby = forms.fields.ChoiceField( # choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),choices= models.Hobby.object.all().values_list("id", "name") label="愛好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) # 上面choices就用到了從數據庫中獲取數據,但這種方式,每次更改數據庫,都需要重啟項目,前端才能更新信息
關於choice的註意事項:
在使用選擇標簽字段時,需要註意choices的選項可以從數據庫中獲取,但時由於時靜態字段,獲取的值無法實時更新,即
中途向數據庫中添加數據,前端不會實時更新,需要重啟項目的問題,但重啟項目明顯不好,故可通過構造函數,每次查詢時,都先去數據庫中查一下使用的數據庫中的字段。將如下的方法也放到類中即可。
方案一
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields[‘hobby3‘].choices = models.Hobby.objects.all().values_list(‘id‘, ‘name‘)
註:其中fields代表的是自定義form類中所有的字段
註:當定義了__init__方法,那麽類中對應字段的choices就可以刪掉了
方案二
from django import forms from django.forms import fields from django.forms import models as form_model class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多選 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 單選
DjangoForm所有的內置字段
Field required=True, 是否允許為空 widget=None, HTML插件 label=None, 用於生成Label標簽或顯示內容 initial=None, 初始值 help_text=‘‘, 幫助信息(在標簽旁邊顯示) error_messages=None, 錯誤信息 {‘required‘: ‘不能為空‘, ‘invalid‘: ‘格式錯誤‘} 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類型Django Form內置字段
2、參數
label 顯示的文本,如input框前的“用戶名:”
initail 初始化的值,即默認值
required 是否必填,required=True
help_text 在input框後的提示信息
error_messages ={"min_length": ‘太短了‘,"required": ‘此數字必須填寫‘} 定義字段,驗證不通過時提示的錯誤信息,格式為字典
widget = forms.widgets.PasswrodInput(attrs={‘class‘: "form_contronl"}) 加插件,可以改變input框的類型,也可以為input標簽加各種屬性,如class,style等等。
validators = [驗證器1,] 裏面可以時django默認的驗證器,也可以自己定義驗證方法,然後放入其中
3、驗證
1、djangoForm組件字段驗證順序
form_obj.is_vaild()==> 通過則將字段加入clean_data字典集合中,如果不通過,則報錯,不會將對應字段名加入到字典中,故後面自定義的方法是拿不到字段的
|
django自己的clean(self) 方法
|
自定義的clean_字段名 的方法 ==> 系統會通過反射檢索是否自定義以clean_開頭的方法,如果有就執行
|
自定義的clean(self) 方法 ==> 這個方法是全局的,對所有的字段校驗,本質是復寫django的clean方法
2、form校驗的幾種方式
validators = [驗證器1,]
2.1、內置的驗證
max_length/min_length
required
2.2、自定義驗證器
方式一、
-
from django.core.Validators import RegexCalidator 有很多這樣的類,這個是正則驗證的模塊 validators=[RegexValidator(r‘^[a-zA-Z][a-zA-Z0-9_@]{4,18}‘, ‘4~18個字符,首字符為字母‘)] 格式為:前部分為正則,後半部分為提示信息
方式二、
-
# 定義函數
from django.core.exceptions import ValidationError def validator_check(value): # 定義方法,設計規則,用於驗證 if ‘sex‘ in value: raise ValidationError(‘含有非法字符‘)
然後在類中的字段 validators中調用方法即可,validators = [validator_check] # 不用加括號
2.3、利用鉤子函數自定義驗證
鉤子,框架級別的東西,流程過程中,預留一些空白,有,就進行驗證,沒有就過
鉤子函數,利用反射hasattr,拿到方法的字符串,有就執行,沒有就過
局部:單獨為某個字段設置
-
clean_字段名(self): 通過校驗:返回當前校驗的值 【for循環校驗】 不通過:raise ValidationError(‘不通過‘) 報錯 為什麽報錯? 因為源碼中通過try捕獲到錯誤,放入到err_diact中,繼續執行其它的程序
全局:所有的字段都校驗完了,才回執行,裏面只有通過的字段
-
clean(self): 通過校驗:返回 self.cleaned_data 不通過: self.add_error(‘re_wd‘, ‘兩次密碼不一致‘) raise ValidationError(‘不通過‘) 報錯
PS:form表單中,如果在form標簽中加入屬性novalidate ,則前端不會做校驗。
三、定義一個form組件
1、創建
可在視圖中創建,但最好在app下單獨建立一個py文件,如myform.py
1.1、導入模塊 from django import forms
1.2、定義form類,繼承forms.Form(只能定義若幹個數據庫表中的字段) 或 forms.Models(可批量定義)
1.3、定義字段(可以是數據庫中的,那麽值就需要通過orm操作獲取數據庫中的值)
示例:
from django import forms from django.forms import widgets # 插件的意思,就是改變【字段】input的類型,如checkbox等等 from django.core.validators import RegexValidator # 用於校驗,正則 class MyForms(forms.Form): username = forms.CharField(label=‘用戶名‘, help_text=‘人生庫端4444‘, # input框右邊的提示語 min_length=4, initial=‘不能小於5個字符‘, error_messages={ ‘min_length‘: ‘長度不符合規則‘ }, widget=widgets.TextInput(attrs={‘class‘: ‘form-control‘}), validators=[RegexValidator(r‘^[a-zA-Z][a-zA-Z0-9_@]{4,18}‘, ‘4~18個字符,首字符為字母‘)] ) password = forms.CharField(label=‘密碼‘, # 密碼默認明文顯示,可通過插件修改 widget=widgets.PasswordInput(attrs={‘class‘: ‘form-control‘}), hobby2 = forms.ChoiceField(label=‘love:下拉多選‘, initial=3, choices=((1, ‘lan‘), (2, ‘qiu‘), (3, ‘dd‘)), widget=widgets.SelectMultiple(attrs={‘class‘: ‘form-control‘}) # 下拉多選 ) )
2、使用
2.1、後端使用:views.py
-
from app_name.myform import MyForm def view_func(request): form_obj = MyForm() 實例化 if request.method == ‘POSt‘: form_obj = MyForm(request.POST) 將接收到的數據傳進去 form_obj.is_vaild() 做校驗,後臺自動完成 form_obj.cleaned_data 字典,已經通過校驗的所有字段,沒有通過,不會放入到裏面 return render(request, ‘index.html‘ , {‘form_obj‘: form_obj}) 註:兩次實例化變量名都為form_obj這樣優化代碼,不然post請求驗證不過,返回錯誤信息,還要寫一個return。
2.2、前端使用
-
{{form_obj.as_p}} 所有的字段生成input框,每個input外面時p標簽 生成的html代碼如下以as_p為例: <P> <lable for=‘{{form_obj.name.id_for_label}}‘>{{form_obj.name.label}}</lable> <input> </p> {{form_obj.as_table/as_ui}} {{form_obj.name}} name字段生成的input框,框的類型type和定義時charField定義有關 {{form_obj.name.label}} name的標簽顯示的文字,如用戶名 {{form_obj.name.id_for_label}} input框的id,用於與label標簽的for關聯 {{form_obj.name.errors}} 該字段後臺驗證返回來的所有的錯誤 {{form_obj.name.errors.0}} 該字段後臺驗證返回來的所有的錯誤的第一個錯誤,所有的錯誤放在一個字典中 {{form_obj.errors}} form表單的所有的錯誤
四、應用bootstrap樣式,如何使用form組件
方式一:一個一個字段寫
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <title>login</title> </head> <body> <div class="container"> <div class="row"> <form action="/login2/" method="post" novalidate class="form-horizontal"> {% csrf_token %} <div class="form-group"> <label for="{{ form_obj.username.id_for_label }}" class="col-md-2 control-label">{{ form_obj.username.label }}</label> <div class="col-md-10"> {{ form_obj.username }} <span class="help-block">{{ form_obj.username.errors.0 }}</span> </div> </div> <div class="form-group"> <label for="{{ form_obj.pwd.id_for_label }}" class="col-md-2 control-label">{{ form_obj.pwd.label }}</label> <div class="col-md-10"> {{ form_obj.pwd }} <span class="help-block">{{ form_obj.pwd.errors.0 }}</span> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">{{ form_obj.gender.label }}</label> <div class="col-md-10"> <div class="radio"> {% for radio in form_obj.gender %} <label for="{{ radio.id_for_label }}"> {{ radio.tag }}{{ radio.choice_label }} </label> {% endfor %} </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <button type="submit" class="btn btn-default">註冊</button> </div> </div> </form> </div> </div> <script src="/static/jquery-3.2.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> </body> </html>
方式二:通過for循環,直接通過field即可
<div class="panel panel-primary"> <div class="panel-heading ">編輯頁面</div> <div class="panel-body"> <form class="form-horizontal " method="post"> {% csrf_token %} {% for field in customer_form_obj %} <div class="form-group "> <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label> <div class="col-sm-10 "> {{ field }} </div> <span class="help-block">{{ field.errors.0 }}</span> </div> {% endfor %} <button type="submit" class="btn btn-lg btn-success btn-block center-block">提交</button> </form> </div> </div>
五、form組件批量添加樣式
可通過重寫form類的init方法來實現。
class LoginForm(forms.Form): username = forms.CharField( min_length=8, label="用戶名", initial="張三", error_messages={ "required": "不能為空", "invalid": "格式錯誤", "min_length": "用戶名最短8位" } ... def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs) for field in iter(self.fields): self.fields[field].widget.attrs.update({ ‘class‘: ‘form-control‘ })批量添加樣式
六、ModelForm
form與model的終極結合,可直接使用models中的所有字段
class BookForm(forms.ModelForm): class Meta: model = models.Book fields = "__all__" labels = { "title": "書名", "price": "價格" } widgets = { "password": forms.widgets.PasswordInput(attrs={"class": "c1"}), }
class Meta:下常用參數:
model = models.Student # 對應的Model中的類 fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段 exclude = None # 排除的字段 labels = None # 提示信息 help_texts = None # 幫助提示信息 widgets = None # 自定義插件 error_messages = None # 自定義錯誤信息
Django框架-Form組件