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阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容