HTML表單
在HTML中,表單是<form>...</form> 之間元素的集合,它們允許訪問者輸入文本、選擇選項、操作對象和控制等等,然后將信息發送回服務器。
某些表單的元素 —— 文本輸入和復選框 —— 非常簡單而且內建於HTML 本身。其它的表單會復雜些;例如彈出一個日期選擇對話框的界面、允許你移動滾動條的界面、使用JavaScript 和CSS 以及HTML 表單<input> 元素來實現操作控制的界面。與<input> 元素一樣,一個表單必須指定兩樣東西:
- 目的地:響應用戶輸入數據的URL
- 方式:發送數據所使用的HTTP 方法
例如,Django Admin 站點的登錄表單包含幾個<input> 元素:type="text" 用於用戶名,type="password" 用於密碼,type="submit" 用於“Log in" 按鈕。它還包含一些用戶看不到的隱藏的文本字段,Django 使用它們來決定下一步的行為。
它還告訴瀏覽器表單數據應該發往<form> 的action 屬性指定的URL —— /admin/,而且應該使用method 屬性指定的HTTP 方法 —— post。
當觸發<input type="submit" value="Log in"> 元素時,數據將發送給/admin/。
Django 中的表單
Django 的Form 類
表單系統的核心部分是Django 的Form 類。Django 的模型描述一個對象的邏輯結構、行為以及展現給我們的方式,與此類似,Form 類描述一個表單並決定它如何工作和展現。
就像模型類的屬性映射到數據庫的字段一樣,表單類的字段會映射到HTML 的<input>表單的元素。
表單字段在瀏覽器中呈現給用戶的是一個HTML 的“widget” —— 用戶界面的一個片段。每個字段類型都有一個合適的默認Widget 類,需要時可以覆蓋。
widget 部件
實例化、處理和渲染表單
在Django 中渲染一個對象時,我們通常:
- 在視圖中獲得它(例如,從數據庫中獲取)
- 將它傳遞給模板上下文
- 使用模板變量將它擴展為HTML 標記
在模板中渲染表單和渲染其它類型的對象幾乎一樣,除了幾個關鍵的差別。
構建一個表單
在html里構建這里就不寫了
1.在app下面新建一個存放Forn類的文件
例如:myforms.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from django import forms
from django.forms import fields
class UserForm(forms.Form):
user = fields.CharField(
max_length=18,
min_length=6,
required = True,
error_messages={
'required':'用戶輸入為空',
'max_length':'用戶輸入太長了',
'min_length':'用戶輸入太短了'
}) #自定義錯誤顯示信息
pwd = fields.CharField(required=True,min_length=8)
age = fields.IntegerField(required=True)
email = fields.EmailField(required=True,min_length=8,
error_messages={
'invalid':"用戶輸入格式錯誤,只要是格式錯誤都用invalid"
})
注:它不包含 <form> 標簽和提交按鈕。我們必須自己在模板中提供它們。
要操作一個通過URL發布的表單,我們要在視圖中實例表單。
2.views.py
from django.shortcuts import render
from django.shortcuts import redirect
from app01.myfoms import UserForm
# Create your views here.
def users(request):
if request.method == "GET":
obj = UserForm() #這就相當於自動生成了一個html
return render(request,'fm.html',{'obj':obj})
elif request.method == 'POST':
fo = UserForm(request.POST)
# print(fo) #前端提交過來的數據
v = fo.is_valid() # 判斷發過來的數據驗證是否通過
if v:
print(fo.cleaned_data) # 查看用戶提交的數據
return redirect('http://www.baidu.com') # 成功驗證跳轉到百度
else:
print(fo.errors) # 查看相應提交數據的錯誤信息
return render(request, 'fm.html', {"obj": fo})
return render(request, 'fm.html')
3.url
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/$', views.users),
]
4.模板html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/users/" method="POST" novalidate>
<p>{{ obj.user }}{{ obj.errors.user.0 }} </p>
<p>{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p>
<p>{{ obj.age }}{{ obj.errors.age.0 }}</p>
<p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
<input type="submit" value="提交">
</form>
/* obj.error.user.0 是顯示在輸入框后顯示錯誤的顯示信息 */
</body>
</html>
提示:
form類實例化的方法和屬性 is_valid() 方法 判斷表單提交是否合法的數據結構,返回True/False cleaned_data 屬性中找到所有合法的表單數據 轉換好為Python 的字典類型。
使用表單模板
例如:
1新建一個表單的py文件 例如:myForms.py 在app下面新建表單內容,
內容如下
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from django import forms
from django.forms import fields
class UserInfo(forms.Form):
user = fields.CharField()
age = fields.IntegerField()
2.視圖views.py
from django.shortcuts import render
from app01 import myForms
# Create your views here.
def index(request):
form = myForms.UserInfo()
if request.method == 'GET':
return render(request,'index.html',{'form':form})
3.路由urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
]
4.模板html
通過視圖把fom表單渲染到模板的html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/index" method="POST">
<table border="1">{{ form.as_table }}</table>
{{ form.as_p }}
<ul>
{{ form.as_ul }}
</ul>
</form>
</body>
</html>
上面用了幾個表單渲染,form.as_p 和 forms.as_ul 和 forms.as_table
表單渲染選項
表單模板的額外標簽
不要忘記,表單的輸出不 包含<form> 標簽,和表單的submit 按鈕
對於<label>/<input> 對,還有幾個輸出選項:
- {{ form.as_table }} 以表格的形式將它們渲染在<tr> 標簽中
- {{ form.as_p }} 將它們渲染在<p> 標簽中
- {{ form.as_ul }} 將它們渲染在<li> 標簽中
注意,你必須自己提供<table> 或<ul> 元素。
表單字段詳解
在django的form表單api,每個字段都是一個類,所有的字段都繼承Field這個類。
提示:多看源碼
Field這個類的常用參數
Field
required=True, 是否允許為空
widget=None, HTML插件 注:重點
label=None, 用於生成Label標簽或顯示內容 類似HTML的label的標簽
initial=None, 初始值 (<input type="text" value="xixi"> 類似html的input框的value值)
help_text='', 幫助信息(在標簽旁邊顯示)
error_messages=None, 錯誤信息 {'required': '不能為空', 'invalid': '格式錯誤'}
show_hidden_initial=False, 是否在當前插件后面再加一個隱藏的且具有默認值的插件(可用於檢驗兩次輸入是否一直)
validators=[], 自定義驗證規則
localize=False, 是否支持本地化
disabled=False, 是否可以編輯
label_suffix=None Label內容后綴
常用字段
ChoiceField 選擇類型的字段(例如,下拉框,等等)
choice[(1,'北京'),(2,'上海'),(3,'深圳')] 這個選項是提供下拉框的內容
表單模板的額外標簽
不要忘記,表單的輸出不 包含<form> 標簽,和表單的submit 按鈕
對於<label>/<input> 對,還有幾個輸出選項:
{{ form.as_table }} 以表格的形式將它們渲染在<tr> 標簽中
{{ form.as_p }} 將它們渲染在<p> 標簽中
{{ form.as_ul }} 將它們渲染在<li> 標簽中
注意,你必須自己提供<table> 或<ul> 元素。
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
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='', 幫助提示
注:choices選項:由可迭代的二元組組成(比如[(A, B), (A, B) ...]),用來給這個字段提供選擇項。如果設置了 choices ,默認表格樣式就會顯示選擇框,而不是標准的文本框,而且這個選擇框的選項就是 choices 中的元組。
每個元組中的第一個元素,是存儲在數據庫中的值;第二個元素是該選項更易理解的描述
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類型
...
重點form字段
Form重點:
- 字段
用於保存正則表達式
ChoiceField *****
MultipleChoiceField
CharField
IntegerField
DecimalField
DateField
DateTimeField
EmailField
GenericIPAddressField
FileField
RegexField
表單字段的HTML插件
所有Field字段本質上封裝了兩個東西,一個是正則表達式,一個是HTML插件
每個表單字段都有一個對應的Widget 類,它對應一個HTML 表單Widget,例如<input type="text">。
在大部分情況下,字段都具有一個合理的默認Widget。例如,默認情況下,CharField 具有一個TextInput Widget,它在HTML 中生成一個<input type="text">。
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
常用widget的選擇HTML插件
不要將Widget 與表單字段搞混淆。表單字段負責驗證輸入並直接在模板中使用。Widget 負責渲染網頁上HTML 表單的輸入元素和提取提交的原始數據。但是,Widget 需要賦值給表單字段。
# 單radio,值為字符串 widgets方式 # user = fields.CharField( # initial=2, # widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) # ) # 單radio,值為字符串 ChoiceField選擇字段方式 # 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 # )
提示:根據自已喜好,選擇用widget插件,還是ChoiceField方式
widget只能生成form相關的html標簽,不能生成div,span標簽等等。。。。
數據實時更新
方式一:(推薦)
在使用選擇標簽時,需要注意choices的選項可以從數據庫中獲取,但是由於是類屬性字段 ***獲取的值無法實時更新***,
那么需要自定義構造方法(__init__)從而達到此目的。
from django.forms import Form
from django.forms import widgets
from django.forms import fields
class MyForm(Form):
user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.Select
)
def __init__(self, *args, **kwargs):
super(MyForm,self).__init__(*args, **kwargs)
# self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
# 或
self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
方式二:
依賴數據庫的model的__str__方法
from django import forms
from django.forms import fields
from django.forms import widgets
from django.forms.models import ModelChoiceField
class FInfo(forms.Form):
authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
# authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
to_field_name='id' #使用id的那列字段作為input框的value值
FORM自定制匹配正則表達式的擴展
方式一
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.CharField(
error_messages={'invalid': '...'},
validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')],
)
字段下的validator
validators=[RegexValidator(r'^[0-9]+$', '請輸入數字')]
validators 自定義匹配正則表達式,可以有多個匹配的正則表達式,匹配順序是從前到后
RegexValidator這個類有兩個參數,第一個是正則表達式,第二個錯誤提示
方式二
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.RegexField(r'^[0-9]+$',error_messages={'invalid': '...'})
正則字段
基於源碼流程的擴展
如果某一個字段不僅只是自定義正則表達式匹配,例如:還有一些用戶注冊的唯一性等等要求,這時候就要基於form源碼來擴展
單子段驗證
重寫:clean_字段名方法
from django import forms
from django.forms import fields
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
class AForm(forms.Form):
username = fields.CharField()
user_id = fields.IntegerField(
widget=widgets.Select(choices=[(0,'張三'),(1,'李四'),(2,'老王'),])
)
# 自定義方法 clean_字段名
# 必須返回值self.cleaned_data['username']
# 如果出錯:raise ValidationError('用戶名已存在')
def clean_username(self):
v = self.cleaned_data['username']
if models.UserInfo.objects.filter(username=v).count():
# 整體錯了
# 自己詳細錯誤信息
raise ValidationError('用戶名已存在')
return v
def clean_user_id(self):
return self.cleaned_data['user_id']
整體錯誤驗證
自定義clean方法
class AForm(forms.Form):
username = fields.CharField()
user_id = fields.IntegerField(
widget=widgets.Select(choices=[(0,'張三'),(1,'李四'),(2,'老王'),])
)
# 自定義方法 clean_字段名
# 必須返回值self.cleaned_data['username']
# 如果出錯:raise ValidationError('用戶名已存在')
def clean_username(self):
v = self.cleaned_data['username']
if models.UserInfo.objects.filter(username=v).count():
# 整體錯了
# 自己詳細錯誤信息
raise ValidationError('用戶名已存在')
return v
def clean_user_id(self):
return self.cleaned_data['user_id']
def clean(self):
value_dict = self.cleaned_data
v1 = value_dict.get('username')
v2 = value_dict.get('user_id')
if v1 == 'root' and v2==1:
raise ValidationError('整體錯誤信息')
return self.cleaned_data
