什麼是表單:
表單是蒐集用戶數據信息的各種表單元素的集合,作用是實現網頁上的數據交互,用戶在網站輸入信息,然後提交到網站服務器端進行處理(如數據錄入和用戶登錄、註冊等)。
用戶表單是web開發的一項基本功能,Django的表單功能有Form類實現,主要分爲兩種:django.forms.Form和django.forms.ModelForm。前者是一個基礎的表單功能,後者是在前者的基礎上結合模型所生成的數據表單。
表單
傳統的表單生成方式是在模板文件總編寫HTML代碼實現,在HTML語言中,表單由標籤實現。表單生成方式如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表單</title>
</head>
<body>
<form>
First name:<br>
<input type="text" name="firstname">
<br>
Last name:<br>
<input type="text" name="lastname">
</form>
</body>
</html>
瀏覽器顯示:
這裏我就要強推w3school,這個網址學前端必備啊,這裏不多說感興趣可以自己去看,也有python都有案例的。
一個完整的表單主要有4個組成部分:提交地址、請求方式、元素控件和提交按鈕。其說明如下:
1、提交地址用於設置用戶提交的表單數據應有那個URL接收和處理,由控件的屬性action決定。當用戶向服務器提交數據時,若屬性action爲空,則提交的數據應有當前的URL來接收和處理,否則網頁會跳轉到屬性action所指向的URL地址。
2、請求方式用於設置表單的提交方式,通常是GET請求或POST請求,由控件的屬性method決定。
3、元素控件是供用戶輸入數據信息的輸入框。由HTML的控件實現,其控件屬性type用於設置輸入框的類型,常用的輸入框類型有文本框、下拉框和複選框等。
4、提交按鈕供用戶數據到服務器,該按鈕也是由HTML的控件實現的。但該按鈕具有一定的特殊性,因此不歸納到元素控件的範圍內。
在模板中直接寫表單文件是一個最簡單最直接的方法,如果表單元素較多,會在無形之中增加模板的代碼量,這樣對日後的維護和更新造成極大的不便。爲了簡化表單的實現過程和提高表單的靈活性,Django提供了完善的表單功能。
在MyDjango的index中添加了空白文件form.py,該文件主要用於編寫表單的實現功能,文件夾可自行命名;同時在文件夾templates中添加模板文件data_form.html,該文件用於將表單的數據顯示到網頁上。最後在文件form.py、views.py和data_form.html中分別添加一下代碼
index(App)下的form.py,沒有這個文件就新建
定義ProductForm表單對象
from django import forms
from .models import *
class ProductForm(forms.Form):
name = forms.CharField(max_length=20, label='名字',)
weight = forms.CharField(max_length=50, label='重量')
size = forms.CharField(max_length=50, label='尺寸')
#設置下拉框的值
choices_list = [(i+1,v['type_name']) for i,v in enumerate(Type.objects.values('type_name'))]
type = forms.ChoiceField(choices=choices_list, label='產品類型')
index(App)下的view.py
將表單ProductForm實例化並將其傳遞到模板中生成網頁內容
from .form import *
def index(request):
product = ProductForm()
return render(request, 'data_form.html', locals())
index下的templates下的html文件data_form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if product.errors %}
<p>
數據出錯啦,錯誤信息:{{ product.errors }}
</p>
{% else %}
<form action="" method="post">
{% csrf_token %}
<table>
{{ product.as_table }}
</table>
<input type="submit" value="提交">
</form>
{% endif %}
</body>
</html>
上述代碼演示了Django內置表單功能的使用方法,主要由form.py、views.py和data_form.html共同實現,說明如下:
1、在form.py中定義表單ProductForm,表單以類的形式表示。在表單中定義了不同類型的類屬性,這些屬性在表單中稱爲表單字段,每個表單字段代表HTML裏的一個控件,這是表單的基本組成單位。
2、在views.py中導入form.py所定義的ProductForm類,在視圖函數index中對ProductForm實例化生成對象product,再將對象product傳遞給模板data_form.html。
3、模板data_form.html將對象product以HTML的<table的形式展現在網頁上。
表單的定義
Django的表單功能主要是通過定義表單類,再由類的實例化生成HTML的表單元素控件,這樣可以在模板中減少HTML的硬編碼。每個HTML的表單元素控件由表單字段來決定,代碼如下:
表單類ProductForm的表單字段name
name = forms.CharField(max_length=20, label='名字',)
表單字段name所生成的HTML元素控件
<tr>
<th><label for="id_name">名字:</label></th>
<td><input type="text" name="name" id="id_name" required maxlength="20"></td>
</tr>
從表單字段轉換HTML元素控件可以發現:
1、字段name的參數label轉換成HTML的標籤
字段 | 說明 |
---|---|
BooleanField | 複選框,如果字段帶有required=True,複選框被勾選上 |
CharField | 文本框,參數max_length和min_length分別設置輸入長度 |
ChoiceField | 下拉框,參數choices設置數據內容 |
TypedChoiceField | 與ChoiceField相似,但比ChoiceField多出參數coerce和empty_value,分別代表強制轉換數據類型和用於表示空值,默認爲空字符串 |
DateField | 文本框,具有驗證日期格式的功能,參數input_formats設置日期格式 |
EmailField | 文本框,驗證輸入數據是否爲合法的郵箱地址。可選參數爲max_length和min_length |
FileField | 文件上傳功能,參數max_length和allow_empty_file分別用於設置文件名的最大長度和文件內容是否爲空 |
FilePathField | 在特定的目錄選擇並上傳文件,參數path是必需參數,參數recursive、match、allow_files和allow_folders爲可選參數 |
FloatField | 驗證數據是否爲浮點數 |
ImageField | 驗證文件是否爲Pillow庫可識別的圖像格式 |
ImtegerField | 驗證數據是否爲整型 |
GenericIPAddressField | 驗證數據是否爲有效數值 |
SlugField | 驗證數據是否只包括字母、數字、下劃線及字符 |
TimeField | 驗證數據是否爲datetime.time或指定特定時間格式的字符串 |
URLField | 驗證數據是否爲有效的URL地址 |
表單字段除了轉換HTML控件之外,還具有一定的數據格式規範,數據格式規範主要由字段類型和字段參數共同實現。每個不同類型的表單字段都有一些自己特殊的參數,但每個表單字段都具有如下表的共同參數:
參數 | 說明 |
---|---|
Required | 輸入數據是否爲空,默認值爲True |
Widget | 設置HTML控件的樣式 |
Label | 用於生成Label標籤或顯示內容 |
Initial | 設置初始值 |
help_text | 設置幫助提示信息 |
error_messages | 設置錯誤信息,以字典格式表示:{‘required’: ‘不能爲空’, ‘invalid’: '格式錯誤‘} |
show_hidden_initial | 值爲True/False,是否在當前差距後面再加一個隱藏的且具有默認值的插件(可用於檢驗兩次輸入值是否一致) |
Validators | 自定義數據驗證規則。以列表格式表示,列表元素爲函數命名 |
Localize | 值爲True/False,是否支持本地和,如不同時區顯示相應的時間 |
Disabled | 值爲True/False,是否可以編輯 |
label_suffix | Label內容後綴,在Label後添加內容 |
根據上面表的參數說明,對form.py的表單ProductForm的字段進行優化:
from django import forms
from .models import *
from django.core.exceptions import ValidationError
#自定義數據驗證函數
def weight_validate(value):
if not str(value).isdigit():
raise ValidationError('請輸入正確的重量')
class ProductForm(forms.Form):
#設置錯誤信息並設置樣式
name = forms.CharField(max_length=20,
label='名字',
widget=forms.widgets.TextInput(attrs={'class': 'c1'}),
error_messages={'required': '名字不能爲空'},)
#使用自定義數據驗證函數
weight = forms.CharField(max_length=50,
label='重量',
validators=[weight_validate])
size = forms.CharField(max_length=50, label='尺寸')
#獲取數據庫數據
choices_list = [(i+1,v['type_name']) for i,v in enumerate(Type.objects.values('type_name'))]
#設置CSS樣式
type = forms.ChoiceField(widget=forms.widgets.Select(attrs={'class':'type','size':'4'}),
choices=choices_list, label='產品類型')
優化的代碼分別使用了參數widget、label、error_messages和validators,這4個參數是實際開發中常用的參數,參數說明如下:
1、參數widget是一個forms.widgets對象,其作用是設置表單字段的CSS樣式。widget的對象類型應與表單字段類型相符合,如果字段類型爲CharField和widget的對象類型爲forms.widgets.TextInput,這兩者的含義與作用是一致的,都代表文本輸入框;如果字段類型爲ChoiceField和widget的對象類型爲forms.widgets.TextInput,前者是下拉選擇框,後者是文本輸入框,那麼在網頁上就會優先顯示爲文本輸入框。
2、參數label會轉換成HTML的標籤
def index(request):
#GET請求
if request.method == 'GET':
product = ProductForm()
return render(request, 'data_form.html', locals())
#POST請求
else:
product = ProductForm(request.POST)
if product.is_valid():
#獲取網頁空間name的數據
#方法一
name = product['name']
#方法二
#cleaned_data將控件name的數據進行清洗,轉換成Python數據類型
cname = product.cleaned_data['name']
return HttpResponse('提交成功')
else:
#將錯誤信息輸出,error_msg是將錯誤信息以json格式輸出
error_msg = product.errors.as_json()
print(error_msg)
return render(request, 'data_form.html', locals())
1、首先判斷用戶的請求方式,不同的請求方式執行不同的程序處理。函數index分別對GET和POST請求做了不同的響應處理。
2、用戶在瀏覽器中訪問http://127.0.0.1:8000/index/,等同於向MyDjango發送一個GET請求,函數index將表單ProductForm實例化並傳遞給模板,由模板引擎生成HTML表單返回給用戶。
3、當用戶在網頁上輸入相關信息後單擊"提交"按鈕,等同於向MyDjango發送一個POST請求,函數index首先獲取表單數據對象product,然後由is_valid()對數據對象product進行數據驗證。
4、如果驗證成功,可以使用product[‘name’]或product.cleaned_data[‘name’]方法來獲取用戶在某個控件上的輸入值。只要將獲取到的輸入值和模型相互使用,就可以實現表單與模型的信息交互。
5、如果驗證失敗,使用errors.as_json()方法獲取驗證失敗的信息,然後將驗證失敗的信息通過模板返回給用戶。
模板data_form.html的表單是使用HTML的table標籤展現在網頁上,除此之外,表單還可以使用其他HTML標籤展現,只需將模板data_form.html的對象product使用以下方法即可生成其他HTML標籤
#將表單生成HTML的ul標籤
{{ product.as_ul }}
#將表單生成HTML的p標籤
{{ product.as_p }}
#生成單個HTML元素控件
{{ product.type }}
#獲取表單字段的參數label屬性值
{{ product.type.label }}
模型與表單
Django的表單分爲兩種:django.forms.Form和django.forms.ModelForm。前者是一個基礎的表單功能,後者是在前者的基礎上結合模型所生成的數據表單。數據表單是將模型的字段轉換成表單的字段,再從表單的字段生成HTML的元素控件,這是日常開發中常用的表單之一。
#forms.py裏面的數據庫表單
class ProductModelForm(forms.ModelForm):
#添加模型外的表單字段
productId = forms.CharField(max_length=20, label='產品序號')
#模型與表單設置
class Meta:
#綁定模型
model = Product
#fields屬性用於設置轉換字段,’__all__'是將全部模型字段轉換成表單字段
#fields = '__all__'
fields = ['name','weight','size','type']
#exclude用於禁止模型字段轉換表單字段
exclude = []
#labels設置HTML元素控件的label標籤
labels = {
'name': '產品名稱',
'weight': '重量',
'size': '尺寸',
'type': '產品類型'
}
#定義widgets,設置表單字段的CSS樣式
widgets = {
'name': forms.widgets.TextInput(attrs={'class': 'cl'}),
}
#定義字段的類型,一般情況下模型的字段會自動轉換成表單字段
field_classes = {
'name': forms.CharField
}
#幫助提示信息
help_texts = {
'name': ''
}
#自定義錯誤信息
error_messages = {
#__all__設置全部錯誤信息
'__all__': {'required': '請輸入內容',
'invalid': '請檢查輸入內容'},
#設置某個字段的錯誤信息
'weight': {'required': '請輸入重複數值',
'invalid': '請檢查數值是否正確'}
}
#自定義表單字段weight的數據清洗
def clean_weight(self):
#獲取字段weight的值
data = self.cleaned_data['weight']
return data+'g'
上述代碼中,表單類ProductModelForm可分爲三大部分:添加模型外的表單字段、模型與表單設置和自定義表單字段weight的數據清洗函數,說明如下:
1、添加模型外的表單字段是在模型已有的字段下添加額外的表單字段。
2、模型與表單設置是將模型的字段轉換成表單字段,由類Meta的屬性實現兩者的字段轉換。
3、自定義表單字段weight的數據清洗函數只只是用於字段weight的數據清洗。
模型字段轉換成表單字段主要在類Meta中實現。
屬性 | 說明 |
---|---|
Model | 必需屬性,用於綁定Model對象 |
Fields | 必須屬性,設置模型內那些字段轉換成表單字段。屬性值爲’all’代表整個模型的字段,若設置一個或多個,使用列表或元組的數據格式表示,列表或元組裏的元素是模型的字段名 |
Exclude | 可選屬性,與fields相反,禁止模型內那些字段轉換成表單字段。屬性值以列表或元組表示,若設置了該屬性,則屬性fields可以不用設置 |
Labels | 可選屬性,設置表單字段裏的參數label。屬性值以字典表示,字典裏的鍵是模型的字段 |
Widgets | 可選屬性,設置表單字段裏的參數widget |
field_classes | 可選屬性,將模型的字段類型重新定義爲表單字段類型,默認情況下,模型字段類型會字段轉換爲表單字段類型 |
help_texts | 可選屬性,設置表單字段裏的參數 |
error_messages | 可選屬性,設置表單字段裏的參數error_messages |
一些較爲特殊的模型字段在轉換表單時會有不同的處理方式。例如模型字段的類型爲AutoField,該字段在表單中不存在對應的表單字段;模型字段類型爲ForeignKey和ManyToManyField,在表單中對應的表單字段爲ModelChoiceField和ModelMultipleChoiceField。
自定義表單字段weight的數據清洗函數是在視圖函數中使用cleaned_data方法時,首先判斷當前清洗的表單字段是否已定義數據清洗函數。例如上述的clean_weight函數,在清洗表單字段weight的數據時會自動執行該自定義函數。在自定義數據清洗函數時,必須以"clean_字段名"的格式作爲函數名,而且函數必須有return返回值。如果在函數中設置ValidationError了異常拋出,那麼該函數可視爲帶有數據驗證的清洗函數。
數據表單的使用
現在使用前面的data_form.html,在MyDjango的url.py和views.py中分別定義新的URL地址和視圖函數:
url.py添加代碼如下:
path('<int:id>.html', views.models_index),
views.py添加代碼如下:
def models_index(request, id):
if request.method == 'GET':
instance = Product.objects.filter(id=id)
# 判斷數據是否存在
if instance:
product = ProductModelForm(instance=instance[0])
else:
product = ProductModelForm()
return render(request, 'data_form.html', locals())
else:
product = ProductModelForm(request.POST)
if product.is_valid():
#獲取weight的數據,並通過clean_weight進行清洗,轉換成Python數據類型
weight = product.cleaned_data['weight']
#數據保存方法一
#直接將數據保存到數據庫
#product.save()
#save方法設置commit=False,將生成數據庫對象product_db,然後對該對象的屬性值修改並保存
product_db = product.save(commit=False)
product_db.name = '我的iPhone'
product_db.save()
#數據保存方法三
#save_m2m()方法用於保存ManyToMany的數據模型
#product.save_m2m()
return HttpResponse('提交成功!weight清洗後的數據爲:'+weight)
else:
#將錯誤信息輸出,error_msg是將錯誤信息以json格式輸出
error_msg = product.errors.as_json()
print(error_msg)
return render(request, 'data_form.html', locals())
1、首先判斷用戶的請求方式,不同的請求方式執行不同的處理程序。代碼分別對GET和POST請求做了不同的響應處理。
2、若當前請求爲GET請求,函數根據URL傳遞的變量id來查找模型Product的數據,如果數據存在,模型的數據以參數的形式傳遞給表單ProductModelForm的參數instance,在生成網頁時,模型數據會填充到對應的元素控件上。
3、若當前請求爲POST請求,函數首先對錶單數據進行驗證,若驗證失敗,則返回失敗信息;若驗證成功,則使用cleaned_data方法對字段weight進行清洗,字段weight清洗由自定義函數clean_weight完成,最後將表單數據保存到數據庫,保存數據有三種方式,具體說明可看代碼註釋,運行結果如下:
這個是我數據庫裏的表
我們試着提交一下,輸入產品序號,
表單的初始化有4種方法,每一種方法都有自己的使用方法:
(1) 在視圖函數中對錶單類進行實例化,可以設置實例化對象的參數initial。例如ProductModelForm(initital={‘name’:value}),參數值以字典的格式表示,字典的鍵爲表單的字段名,這種方法適用於所有表單類。
(2) 在表單類中進行實例化,如果初始化的數據是一個模型對象的數據,可以設置參數instanse,這種方法只是用於ModelForm,如ProductModelForm(instanse=instanse)。
(3) 定義表單字段時,可以對錶單字段設置初始化參數initial,此方法不適用於ModelForm,如name=forms.CharField(initial=value)。
(4) 重寫表單類的初始化函數__init__(),適用於所有表單類,如在初始化函數__init__()中設置self.fields[‘name’].initial=value。
下拉框的數據是一個模型Type對象,而下拉框是由模型Product的外鍵type所生成的,外鍵type指向模型Type。因此,要解決下拉框的數據問題,可以從定義模型或者定義表單這兩方面解決。
定義模型是在定義模型Type時,設置該模型的返回值。當有外鍵指向模型Type時,模型Type會將返回值返回給外鍵。在模型中通過重寫__str__函數可以設置模型的返回值:
class Type(models.Model): #創建產品分類表
id = models.AutoField(primary_key=True) #數據類型爲整形且爲主鍵
type_name = models.CharField(max_length=20)
#設置返回值,若不設置,則默認返回Type對象
def __str__(self):
return self.type_name
如果存在多個下拉框,而且每個下拉框的數據分別取不同一個模型的不同字段,那麼重寫__str__函數可能不太可行。遇到這種情況,可以在定義表單類的時候重寫初始化函數__init__():
class ProductModelForm(forms.ModelForm):
#方法四:重寫ProductModelForm類的初始化函數__init__
def __init__(self, *args, **kwargs):
super(ProductModelForm, self).__init__(*args, **kwargs)
#設置下拉框的數據
type_obj = Type.objects.values('type_name')
choices_list = [(i+1,v['type_name']) for i,v in enumerate(Type.objects.values('type_name'))]
self.fields['type'].choices = choices_list
#初始化字段name
self.fields['name'].initial = '我的手機'
實質上數據保存只有save()和save_m2m()方法實現,在上述代碼中所演示的三種保存方式,前兩者是save()的參數commit的不同而導致保存方式有所不同。如果參數commit爲True,直接將表單數據保存到數據庫;如果參數commit爲False,這時將生成一個數據庫對象,然後可以對該對象進行增刪改查等數據操作,再將修改後的數據保存到數據庫中。注意的是save()只適合於將數據保存在非多對多數據關係的數據表,而save_m2m()只適合將數據保存在多對多數據關係的數據表。
本篇博客參考Django的建站基礎表單與模型