forms.Form
在之前的示例HTML頁面中利用form表單向后端提交數據時,都會寫一些獲取用戶輸入的標簽並且用form標簽把它們包起來。
與此同時很多時候都需要對用戶的輸入做校驗,比如校驗用戶是否輸入,輸入的長度和格式等是否符合條件。如果用戶輸入的內容有錯誤就需要在頁面上相應的位置顯示對應的錯誤信息。
Django form組件就實現了上面所述的功能。
總 結一下,其實form組件的主要功能如下:
- 生成頁面可用的HTML標簽
- 對用戶提交的數據進行校驗
- 保留上次輸入內容
常用字段
.CharField() 輸入框
屬性 | 描述 | 屬性 | 描述 |
---|---|---|---|
label | 設置label標簽 | initial | 設置默認值 |
min_length | 最小長度 | widget | 輸入框屬性 (forms.PasswordInput 表示密碼輸入框) |
error_messages | 重寫錯誤類型 使用字典可以重寫 |
required | True表示必填;Flase表示可以為空 |
validators | 自定義校驗器函數列表 | max_length | 超過做大限度之后,不能繼續輸入 |
.ChoiceField() 單選框
屬性 | 描述 | 屬性 | 描述 |
---|---|---|---|
label | choices | 設置選項((),(),()) | |
initial | 設置默認值[] | error_messages | 重定義錯誤提示 |
required | 是否必填 | widget | 選擇框類型 |
.MultipleChoiceField() 多選框
屬性 | 描述 | 屬性 | 描述 |
---|---|---|---|
label | choices | 設置選項((),(),()) | |
initial | 設置默認值[] | error_messages | 重定義錯誤提示 |
required | 是否必填 | widget | 選擇框類型 |
.ModelChoiceField()和.ModelMultipleChoiceField()
在.ChoiceField()和.MultipleChoiceField()的升級款,不需要重啟項目,就能夠從數據庫實時將數據的數據提取出來。
特殊屬性:queryset,查詢的是數據庫的數據
【所有字段屬性】
所有字段
'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',
# 對應屬性
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類型
使用流程
視圖中
第一步:引入forms插件from django import forms
第二步:創建表單類
第三步:創建視圖函數,實例化表單對象,傳給模板
from django import forms
# 創建表單類
class Formtable(forms.Form):
user = forms.CharField(
label='用戶名',
min_length=6,
required=True,
error_messages={
'required':'該字段不得為空',
'min_length':'用戶名至少六位'
}
)
pwd = forms.CharField(
label='密碼',
widget=forms.PasswordInput,
required=True,
min_length=8,
max_length=12,
error_messages={
'required': '該字段不得為空',
'min_length':'密碼長度不能少於8位',
'max_length':'密碼長度不能大於12位',
}
)
gender = forms.ChoiceField(
label='性別',
choices=((1,'男'),(2,'女')),
initial=[1,],
widget=forms.RadioSelect,
)
hobby = forms.MultipleChoiceField(
label='興趣愛好',
choices=choices=models.Hobby.objects.values_list('id','type'), //從數據庫中提取數據
initial= [1,2],
widget=forms.CheckboxSelectMultiple,
)
S
hobby = forms.ModelMultipleChoiceField(
label='興趣愛好',
queryset=models.Hobby.objects.all(),
initial=[1, ],
widget=forms.CheckboxSelectMultiple,
)
def form_table2(request):
form_obj = Formtable()
if request.method == "POST":
form_obj=Formtable(data=request.POST) # data作用是提取POST中的值
if form_obj.is_valid() : # .is_valid判斷數據是否可用,做校驗,里邊包含錯誤信息
# 判斷成功,插入到數據庫
return HttpResponse("數據已保存")
return render(request, 'formtable2.html', {'form_obj':form_obj})
注意: hobby雖然是能夠從數據庫中提取信息,但有一個問題是,當對數據庫中的數據進行修改后,只有重新啟動項目,才能夠顯示最新數據,要解決這個問題,需要使用.ModelMultipleChoiceField()
hobby = forms.ModelMultipleChoiceField(
label='興趣愛好',
queryset=models.Hobby.objects.all(),
initial=[1, ],
widget=forms.CheckboxSelectMultiple,
)
# 視圖函數
def formtable(request):
form_obj = Formtable()
if request.method == 'POST':
form_obj = Formtable(request.POST)
if form_obj.is_valid():
print(form_obj.cleaned_data) # 看看有哪些信息
user = form_obj.cleaned_data.get('name')
pwd = form_obj.cleaned_data.get('pwd')
# 往數據庫寫數據
return HttpResponse('注冊成功')
else:
all_error = form_obj.errors.get("__all__")
return render(request, 'Formtable.html', locals())
return render(request,'zhuce.html',{'form_obj':form_obj})
HTML中
第一步:創建form標簽
第二步:接收form對象
【常用屬性】
{{ form_obj.as_p }} # 展示所有的字段
{{ form_obj.user }} # input框
{{ form_obj.user.label }} # label標簽的中文提示
{{ form_obj.user.id_for_label }} # input框的id
{{ form_obj.user.errors }} # 一個字段的錯誤信息
{{ form_obj.user.errors.0 }} # 一個字段的第一個錯誤信息
{{ form_obj.errors }} # 所有字段的錯誤
【示例】
# 方式一:自動生成
<form action="" method="post">
{% csrf_token %}
{{ form_obj.as_p }}
// as_p的意思是每一個字段占一個p標簽,還有as_table,as_errord等等
<button>注冊</button>
</form>
# 方式二:單個生成
<form action="" method="post" novalidate>
{% csrf_token %}
<p>
<label for="{{ form_obj.user.id_for_label }}">{{ form_obj.user.label }}:</label>
{{ form_obj.user}}
<span>{{ form_obj.user.errors.0 }}</span>
</p>
<p>
<label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}:</label>
{{ form_obj.pwd}}
<span>{{ form_obj.pwd.errors.0 }}</span>
</p>
<p>
<label> {{ form_obj.gender.label }}:</label>
{{ form_obj.gender.0}} {{ form_obj.gender.1}}
</p>
<p>
<label>{{ form_obj.hobby.label }}:</label>
{% for i in form_obj.hobby %}
{{ i }}
{% endfor %}
</p>
<button>注冊</button>
</form>
校驗
方式一:自定義校驗函數
# 第一步:導入ValidationError模塊
from django.core.exceptions import ValidationError
# 第二步:自定義校驗函數和校驗規則,沒有通過校驗,拋出ValidationError異常
def checkuser(value):
if 'guest' in value:
raise ValidationError("不能使用guest用戶名")
# 第三步:添加到要檢驗的Form字段中的validators屬性
class Formtable(forms.Form):
user = forms.CharField(
label='用戶名',
min_length=5,
required=True,
error_messages={
'required':'該字段不得為空',
'min_length':'用戶名至少五位'
},
validators=[checkuser]
)
方式二:使用內置的validators
# 第一步,導入相應的校驗器,舉例使用的是正則校驗器,格式是('正則表達式','錯誤信息')
from django.core.validators import RegexValidator
# 第二步:在要檢驗的Form字段中的validators屬性,直接添加校驗器和校驗規則
pwd = forms.CharField(
label='密碼',
widget=forms.PasswordInput,
required=True,
min_length=8,
max_length=12,
error_messages={
'required': '該字段不得為空',
'min_length':'密碼長度不能少於8位',
'max_length':'密碼長度不能大於12位',
},
validators=[RegexValidator(r"^[A-Z]",'請以大寫字母開頭')]
)
校驗流程
第一步:is_valid()中要執行full_clean():
- self._errors ={} 定義一個存放錯誤信息的字典
- self.cleaned_data = {} 定義一個存放有效數據的字典
第二步:執行self._clean_fields()
- 先執行內置的校驗和校驗器的校驗
- 有局部鈎子,執行局部鈎子
第三步:執行 self.clean() 全局鈎子
局部鈎子
根據_clean_fields()
中定義的那樣 if hasattr(self, 'clean_%s' % name):
,局部鈎子的格式就是以clean_
開頭,定義局部鈎子就是使用clean_字段名
,獲取字段的值使用的是self.clean_data.get('字段名')
class Formtable(forms.Form):
# 定義字段
user = forms.CharField(
label='用戶名',
min_length=5,
required=True,
error_messages={
'required':'該字段不得為空',
'min_length':'用戶名至少五位'
},
)
# 定義該字段的局部鈎子
def clean_user(self):
# 局部鈎子
# 不通過校驗,拋出異常,將異常信息以 {'name':value} 寫入errors字典中
# 通過校驗,返回當前字段的值,把name返回到clean_data,將name寫入clean_data字典中
username = self.cleaned_data.get('user')
print(username,type(username))
if username == 'guest':
raise ValidationError("不能使用guest用戶名")
return username
全局鈎子
可以對多個字段進行額外的校驗,比如注冊時使用的確認密碼,需要與第一次的密碼進行匹配,這是就要用到全局鈎子。
# 定義Form組件
class Formtable(forms.Form):
name = forms.CharField(label='用戶名',
error_messages={'required': '該字段是必填的~',},
validators=[usercheck,])
pwd = forms.CharField(label='密碼',
widget=forms.PasswordInput,
min_length=8,
error_messages={'required': '該字段是必填的~','min_length': '至少是8位'},
validators=[pwdcheck,])
re_pwd = forms.CharField(label='確認密碼',
widget=forms.PasswordInput,
min_length=8,
error_messages={'required': '該字段是必填的~','min_length': '至少是8位'}
)
# 定義全局鈎子
# 重寫clean方法
# 校驗失敗,拋異常,將異常信息以{'__all__':[value,]} 寫入 errors 字典中
# 校驗成功,返回clean_data字典
# 拋出異常的類型為ValidationError
def clean(self):
# 程序能走到該函數,前面校驗已經通過了,所以可以從cleaned_data中取出密碼和確認密碼
pwd = self.cleaned_data.get('pwd')
repwd = self.cleaned_data.get('re_pwd')
# 進行兩次密碼校驗
if pwd == repwd:
# 通過,直接返回cleaned_data
return self.cleaned_data
else:
# 失敗,拋異常(ValidationError)
self.add_error('re_pwd','兩次密碼不一致')
raise ValidationError('兩次密碼不一致')
# 全局鈎子的錯誤提示不屬於任何字段,而是在保存在__all__中,顯示可以有兩種方式:
方式一:可以把它添加到一個字段中,然后顯示出來,例如上代碼else所示,以通過該字段顯示錯誤信息
方式二:在前端使用{{ myforms.errors.__all__.0 }}