Django Forms 基本總結(一)

Django提供了一系列的工具和庫,幫助你建立表單來接受來自網站訪客的輸入,然后 對輸入進行處理和響應。

1.HTML forms

在HTML中,一個form表單代表位于<form>...<form>標簽中的一系列元素,允許訪客輸入文本,選擇選項,操作對象或控件等等,然后把結果發回到服務器端。
一個form必須指定兩個重要的事情:

where: url標識用戶輸入的數據要返回到哪里
how: http method標識數據返回的方式

舉例例子,在登錄表單中,有用戶名,密碼,及登錄按鈕等控件。在用戶輸入數據登錄后,用戶數據會被發送到<form>元素的action屬性標識的url。發送方式通過method屬性指定的http機制--post.

2.Django’s role in forms

django處理forms中三種不同部分的工作:

準備和重組數據為渲染做準備
為數據創建html forms
接收處理提交的表單和客戶端提交的數據

這些都可以手動代碼實現,但是django可以為你做好一切。

3. Forms in Django

核心是Form類。就像Django的model一樣 描述一個對象的邏輯結構,它的行為,以及它的部分表示給我們的方式,一個Form類描述一個form,并決定他是如何工作和呈現的。就像model類的一個字段映射一個數據庫字段一樣,一個form類的字段映射到html form中一個<input>元素。一個form字段本身就是一個類,它們管理表單數據并且在一個表單提交時執行驗證。
當我們渲染一個類時,我們通常L:

1.在view中獲取到它
2.傳入到template context中
3.使用模板變量將其擴展到HTML標記

在模板中呈現表單涉及到幾乎與呈現任何其他對象相同的工作,當然也有一些不同的地方。比如form表單在讓用戶填充時,可以是空的。

4.Building a form

from django import forms
class NameForm(forms.Form):
      your_name = forms.CharField(label='Your name', max_length=100)

Form實例有一個名為is_valid()的方法,用來對所有字段進行有效性驗證。當這個方法被調用時,如果每個字段都包含由有效數據,他將:

1.返回True
2.將數據放置在名為cleaned_data屬性中。

整個form在初次渲染后的像下面這樣:

<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required />

注意它并不包含<form>標簽,也沒有提交按鈕,我們必須在模板中自己提供這些。

5.More about Django Form classes

所有的form類都是django.forms.Form的子類,包括ModelForm.事實上,如果你的form被用于直接增加或者編輯一個django的model實例,ModelForm可以幫你節省很多時間,代碼實現。因為它通過一個Model類的適當的字段和屬性構建一個form。
注意,綁定的和未綁定的表單實例的區別很重要:

1.一個未綁定的表單沒有關聯的數據,當渲染給用戶時,通常是空的或者包含一些默認的數據。
2.一個捆綁的表單提交數據后,可以檢測數據的有效性,如果包含無效數據的表單被渲染后,它會包含一些內聯的錯誤信息告訴用戶哪些數據正確。

form 的is_bound屬性 會告訴你一個表單是否有關聯的數據

6. Working with form templates

讓你的form放入到模板中需要做的就是將form實例放入到模板內容中,所以如果你的表單叫form,{{ form }}將把它用適當的<label><input>渲染。不要忘了一個表單的輸出不包含被包裹的<form>標簽,也沒有表單的提交組件,需要自己提供。
還有一些輸出選項,如下:

1.{{form.as_table }}將作為表格的單元格被包裹在<tr>標簽中渲染

  1. {{ form.as_p }} 將被包裹在< p>標簽中渲染
  2. {{ form.as_ul }} 將被包裹在< li>標簽中被渲染

記住,你必須自己提供外圍的<table>或者< ul>元素
當然,我們也可以手動的渲染字段。

{{ 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>

也可以循環逐個的渲染

{% 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是一個抽象層,用于在同一頁面上處理多個表單.舉例:

>>> from django import forms
>>> class ArticleForm(forms.Form):
... title = forms.CharField()
... pub_date = forms.DateField()

您可能希望允許用戶同時創建幾篇文章.如下:

>>> from django.forms import formset_factory
>>> ArticleFormSet = formset_factory(ArticleForm)

ArticleFormSet就是一個formset.你可以利用它迭代其中的表單展示給用戶。

>>> formset = ArticleFormSet()
>>> for form in formset:
... print(form.as_table())

所顯示的空表單的數量由extra的參數,默認值為1.下面會展示兩個空的表單

>>> 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())

上面會展示三組表單,其中第一組表單會默認填充initial數據,另外兩組表單為空。
對formsetfactory()的maxnum參數提供了限制將顯示表單的數量的能力。

>>> 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 驗證

對Formset的驗證與常規表單幾乎相同.在formset上有一個is_valid的方法 提供一種方便的方式來驗證表單中的所有表單:

>>> 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

您可能已經注意到額外的數據(form-TOTAL_FORMS, form-INITIAL_FORMS和form-MAX_NUM_FORMS)在上面的formset的數據中是必需的。如果不提供此管理數據,則會出現異常.

>>> 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水平上的驗證。舉例:

>>> 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方法調用之后調用,所有爆出的錯誤都可以用non_form_errors()方法看到。

8.Creating forms from models

8.1 ModelForm

如果你正在構建一個數據庫驅動的應用程序,那么你可能會有與Django models密切相關的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()

當我們以commit=False的形式調用save方法后,數據不會保存到數據庫中,我們還可以對其中的數據字段進行修改,再次調用save()才會保存到數據庫中,如果表單中含有many_to_many類型的字段,為了保存數據還需要調用save_m2m方法。該方法只有在commit=False才需要調用,其他時候無需調用。
另外,django使用下面的規則:如果你在model的字段中設置了editable=False,那么,任何通過ModelForm創建的form都不會包含它。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容