Django提供了一系列的工具和庫,幫助你建立表單來接受來自網(wǎng)站訪客的輸入,然后 對(duì)輸入進(jìn)行處理和響應(yīng)。
1.HTML forms
在HTML中,一個(gè)form表單代表位于<form>...<form>標(biāo)簽中的一系列元素,允許訪客輸入文本,選擇選項(xiàng),操作對(duì)象或控件等等,然后把結(jié)果發(fā)回到服務(wù)器端。
一個(gè)form必須指定兩個(gè)重要的事情:
where: url標(biāo)識(shí)用戶輸入的數(shù)據(jù)要返回到哪里
how: http method標(biāo)識(shí)數(shù)據(jù)返回的方式
舉例例子,在登錄表單中,有用戶名,密碼,及登錄按鈕等控件。在用戶輸入數(shù)據(jù)登錄后,用戶數(shù)據(jù)會(huì)被發(fā)送到<form>元素的action屬性標(biāo)識(shí)的url。發(fā)送方式通過method屬性指定的http機(jī)制--post.
2.Django’s role in forms
django處理forms中三種不同部分的工作:
準(zhǔn)備和重組數(shù)據(jù)為渲染做準(zhǔn)備
為數(shù)據(jù)創(chuàng)建html forms
接收處理提交的表單和客戶端提交的數(shù)據(jù)
這些都可以手動(dòng)代碼實(shí)現(xiàn),但是django可以為你做好一切。
3. Forms in Django
核心是Form類。就像Django的model一樣 描述一個(gè)對(duì)象的邏輯結(jié)構(gòu),它的行為,以及它的部分表示給我們的方式,一個(gè)Form類描述一個(gè)form,并決定他是如何工作和呈現(xiàn)的。就像model類的一個(gè)字段映射一個(gè)數(shù)據(jù)庫字段一樣,一個(gè)form類的字段映射到html form中一個(gè)<input>元素。一個(gè)form字段本身就是一個(gè)類,它們管理表單數(shù)據(jù)并且在一個(gè)表單提交時(shí)執(zhí)行驗(yàn)證。
當(dāng)我們渲染一個(gè)類時(shí),我們通常L:
1.在view中獲取到它
2.傳入到template context中
3.使用模板變量將其擴(kuò)展到HTML標(biāo)記
在模板中呈現(xiàn)表單涉及到幾乎與呈現(xiàn)任何其他對(duì)象相同的工作,當(dāng)然也有一些不同的地方。比如form表單在讓用戶填充時(shí),可以是空的。
4.Building a form
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
Form實(shí)例有一個(gè)名為is_valid()的方法,用來對(duì)所有字段進(jìn)行有效性驗(yàn)證。當(dāng)這個(gè)方法被調(diào)用時(shí),如果每個(gè)字段都包含由有效數(shù)據(jù),他將:
1.返回True
2.將數(shù)據(jù)放置在名為cleaned_data屬性中。
整個(gè)form在初次渲染后的像下面這樣:
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required />
注意它并不包含<form>標(biāo)簽,也沒有提交按鈕,我們必須在模板中自己提供這些。
5.More about Django Form classes
所有的form類都是django.forms.Form的子類,包括ModelForm.事實(shí)上,如果你的form被用于直接增加或者編輯一個(gè)django的model實(shí)例,ModelForm可以幫你節(jié)省很多時(shí)間,代碼實(shí)現(xiàn)。因?yàn)樗ㄟ^一個(gè)Model類的適當(dāng)?shù)淖侄魏蛯傩詷?gòu)建一個(gè)form。
注意,綁定的和未綁定的表單實(shí)例的區(qū)別很重要:
1.一個(gè)未綁定的表單沒有關(guān)聯(lián)的數(shù)據(jù),當(dāng)渲染給用戶時(shí),通常是空的或者包含一些默認(rèn)的數(shù)據(jù)。
2.一個(gè)捆綁的表單提交數(shù)據(jù)后,可以檢測數(shù)據(jù)的有效性,如果包含無效數(shù)據(jù)的表單被渲染后,它會(huì)包含一些內(nèi)聯(lián)的錯(cuò)誤信息告訴用戶哪些數(shù)據(jù)正確。
form 的is_bound屬性 會(huì)告訴你一個(gè)表單是否有關(guān)聯(lián)的數(shù)據(jù)
6. Working with form templates
讓你的form放入到模板中需要做的就是將form實(shí)例放入到模板內(nèi)容中,所以如果你的表單叫form,{{ form }}將把它用適當(dāng)?shù)?lt;label><input>渲染。不要忘了一個(gè)表單的輸出不包含被包裹的<form>標(biāo)簽,也沒有表單的提交組件,需要自己提供。
還有一些輸出選項(xiàng),如下:
1.{{form.as_table }}將作為表格的單元格被包裹在<tr>標(biāo)簽中渲染
- {{ form.as_p }} 將被包裹在< p>標(biāo)簽中渲染
- {{ form.as_ul }} 將被包裹在< li>標(biāo)簽中被渲染
記住,你必須自己提供外圍的<table>或者< ul>元素
當(dāng)然,我們也可以手動(dòng)的渲染字段。
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="{{ form.message.id_for_label }}">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="{{ form.sender.id_for_label }}">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
{{ form.cc_myself }}
</div>
也可以循環(huán)逐個(gè)的渲染
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
7.Further topics
7.1 Formset
formset是一個(gè)抽象層,用于在同一頁面上處理多個(gè)表單.舉例:
>>> from django import forms
>>> class ArticleForm(forms.Form):
... title = forms.CharField()
... pub_date = forms.DateField()
您可能希望允許用戶同時(shí)創(chuàng)建幾篇文章.如下:
>>> from django.forms import formset_factory
>>> ArticleFormSet = formset_factory(ArticleForm)
ArticleFormSet就是一個(gè)formset.你可以利用它迭代其中的表單展示給用戶。
>>> formset = ArticleFormSet()
>>> for form in formset:
... print(form.as_table())
所顯示的空表單的數(shù)量由extra的參數(shù),默認(rèn)值為1.下面會(huì)展示兩個(gè)空的表單
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
>>> import datetime
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
>>> formset = ArticleFormSet(initial=[
... {'title': 'Django is now open source',
... 'pub_date': datetime.date.today(),}
... ])
>>> for form in formset:
... print(form.as_table())
上面會(huì)展示三組表單,其中第一組表單會(huì)默認(rèn)填充initial數(shù)據(jù),另外兩組表單為空。
對(duì)formsetfactory()的maxnum參數(shù)提供了限制將顯示表單的數(shù)量的能力。
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1)
>>> formset = ArticleFormSet()
>>> for form in formset:
... print(form.as_table())
7.2 驗(yàn)證
對(duì)Formset的驗(yàn)證與常規(guī)表單幾乎相同.在formset上有一個(gè)is_valid的方法 提供一種方便的方式來驗(yàn)證表單中的所有表單:
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm)
>>> data = {
... 'form-TOTAL_FORMS': '1',
... 'form-INITIAL_FORMS': '0',
... 'form-MAX_NUM_FORMS': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
True
您可能已經(jīng)注意到額外的數(shù)據(jù)(form-TOTAL_FORMS, form-INITIAL_FORMS和form-MAX_NUM_FORMS)在上面的formset的數(shù)據(jù)中是必需的。如果不提供此管理數(shù)據(jù),則會(huì)出現(xiàn)異常.
>>> data = {
... 'form-0-title': 'Test',
... 'form-0-pub_date': '',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
Traceback (most recent call last):
...
django.forms.utils.ValidationError: ['ManagementForm data is missing or has been tampered with']
7.3 定制formset validation
formset 有和Form 類相似的clean方法,該方法可用來定義你自己在formset水平上的驗(yàn)證。舉例:
>>> from django.forms import BaseFormSet
>>> from django.forms import formset_factory
>>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
... def clean(self):
... """Checks that no two articles have the same title."""
... if any(self.errors):
... # Don't bother validating the formset unless each form is valid on its own
... return
... titles = []
... for form in self.forms:
... title = form.cleaned_data['title']
... if title in titles:
... raise forms.ValidationError("Articles in a set must have distinct titles.")
... titles.append(title)
>>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)
>>> data = {
... 'form-TOTAL_FORMS': '2',
... 'form-INITIAL_FORMS': '0',
... 'form-MAX_NUM_FORMS': '',
... 'form-0-title': 'Test',
... 'form-0-pub_date': '1904-06-16',
... 'form-1-title': 'Test',
... 'form-1-pub_date': '1912-06-23',
... }
>>> formset = ArticleFormSet(data)
>>> formset.is_valid()
False
>>> formset.errors
[{}, {}]
>>> formset.non_form_errors()
['Articles in a set must have distinct titles.']
formset的clean方法在所有的form的clean方法調(diào)用之后調(diào)用,所有爆出的錯(cuò)誤都可以用non_form_errors()方法看到。
8.Creating forms from models
8.1 ModelForm
如果你正在構(gòu)建一個(gè)數(shù)據(jù)庫驅(qū)動(dòng)的應(yīng)用程序,那么你可能會(huì)有與Django models密切相關(guān)的Form.舉例:
>>> from django.forms import ModelForm
>>> from myapp.models import Article
# Create the form class.
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
... fields = ['pub_date', 'headline', 'content', 'reporter']
# Creating a form to add an article.
>>> form = ArticleForm()
# Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1)
>>> form = ArticleForm(instance=article)
8.2 Field types
from django.db import models
from django.forms import ModelForm
TITLE_CHOICES = (
('MR', 'Mr.'),
('MRS', 'Mrs.'),
('MS', 'Ms.'),
)
class Author(models.Model):
name = models.CharField(max_length=100)
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
birth_date = models.DateField(blank=True, null=True)
def __str__(self): # __unicode__ on Python 2
return self.name
class Book(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ['name', 'title', 'birth_date']
class BookForm(ModelForm):
class Meta:
model = Book
fields = ['name', 'authors']
上面的Form等同于下面的形式(唯一的不同是save()):
from django import forms
class AuthorForm(forms.Form):
name = forms.CharField(max_length=100)
title = forms.CharField(
max_length=3,
widget=forms.Select(choices=TITLE_CHOICES),
)
birth_date = forms.DateField(required=False)
class BookForm(forms.Form):
name = forms.CharField(max_length=100)
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
8.3 The save() method
>>> from myapp.models import Article
>>> from myapp.forms import ArticleForm
# Create a form instance from POST data.
>>> f = ArticleForm(request.POST)
# Save a new Article object from the form's data.
>>> new_article = f.save()
# Create a form to edit an existing Article, but use
# POST data to populate the form.
>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(request.POST, instance=a)
>>> f.save()
需要注意的是:
# Create a form instance with POST data.
>>> f = AuthorForm(request.POST)
# Create, but don't save the new author instance.
>>> new_author = f.save(commit=False)
# Modify the author in some way.
>>> new_author.some_field = 'some_value'
# Save the new instance.
>>> new_author.save()
# Now, save the many-to-many data for the form.
>>> f.save_m2m()
當(dāng)我們以commit=False的形式調(diào)用save方法后,數(shù)據(jù)不會(huì)保存到數(shù)據(jù)庫中,我們還可以對(duì)其中的數(shù)據(jù)字段進(jìn)行修改,再次調(diào)用save()才會(huì)保存到數(shù)據(jù)庫中,如果表單中含有many_to_many類型的字段,為了保存數(shù)據(jù)還需要調(diào)用save_m2m方法。該方法只有在commit=False才需要調(diào)用,其他時(shí)候無需調(diào)用。
另外,django使用下面的規(guī)則:如果你在model的字段中設(shè)置了editable=False,那么,任何通過ModelForm創(chuàng)建的form都不會(huì)包含它。