使用基于类的视图处理表单

表单的处理通常有3 个步骤:

  • 初始的的GET (空白或预填充的表单)
  • 带有非法数据的POST(通常重新显示表单和错误信息)
  • 带有合法数据的POST(处理数据并重定向)

你自己实现这些功能经常导致许多重复的样本代码(参见在视图中使用表单)。为了避免这点,Django 提供一系列的通用的基于类的视图用于表单的处理。

基本的表单

根据一个简单的联系人表单:

  1. #forms.py
  2. from django import forms
  3. class ContactForm(forms.Form):
  4. name = forms.CharField()
  5. message = forms.CharField(widget=forms.Textarea)
  6. def send_email(self):
  7. # send email using the self.cleaned_data dictionary
  8. pass

可以使用FormView来构造其视图:

  1. #views.py
  2. from myapp.forms import ContactForm
  3. from django.views.generic.edit import FormView
  4. class ContactView(FormView):
  5. template_name = 'contact.html'
  6. form_class = ContactForm
  7. success_url = '/thanks/'
  8. def form_valid(self, form):
  9. # This method is called when valid form data has been POSTed.
  10. # It should return an HttpResponse.
  11. form.send_email()
  12. return super(ContactView, self).form_valid(form)

注:

  • FormView继承TemplateResponseMixin所以这里可以使用template_name
  • form_valid()的默认实现只是简单地重定向到success_url

模型的表单

通用视图在于模型一起工作时会真正光芒四射。这些通用的视图将自动创建一个ModelForm,只要它们能知道使用哪一个模型类:

  • 如果给出model属性,则使用该模型类。
  • 如果get_object() 返回一个对象,则使用该对象的类。
  • 如果给出queryset,则使用该查询集的模型。

模型表单提供一个form_valid() 的实现,它自动保存模型。如果你有特殊的需求,可以覆盖它;参见下面的例子。

你甚至不需要为CreateViewUpdateView提供success_url —— 如果存在它们将使用模型对象的get_absolute_url()

如果你想使用一个自定义的ModelForm(例如添加额外的验证),只需简单地在你的视图上设置form_class

当指定一个自定义的表单类时,你必须指定模型,即使form_class 可能是一个ModelForm

首先我们需要添加get_absolute_url() 到我们的Author 类中:

  1. #models.py
  2. from django.core.urlresolvers import reverse
  3. from django.db import models
  4. class Author(models.Model):
  5. name = models.CharField(max_length=200)
  6. def get_absolute_url(self):
  7. return reverse('author-detail', kwargs={'pk': self.pk})

然后我们可以使用CreateView 机器伙伴来做实际的工作。注意这里我们是如何配置通用的基于类的视图的;我们自己没有写任何逻辑:

  1. #views.py
  2. from django.views.generic.edit import CreateView, UpdateView, DeleteView
  3. from django.core.urlresolvers import reverse_lazy
  4. from myapp.models import Author
  5. class AuthorCreate(CreateView):
  6. model = Author
  7. fields = ['name']
  8. class AuthorUpdate(UpdateView):
  9. model = Author
  10. fields = ['name']
  11. class AuthorDelete(DeleteView):
  12. model = Author
  13. success_url = reverse_lazy('author-list')

这里我们必须使用reverse_lazy() 而不是reverse,因为在该文件导入时URL 还没有加载。

fields 属性的工作方式与ModelForm 的内部Meta类的fields 属性相同。除非你用另外一种方式定义表单类,该属性是必须的,如果没有将引发一个ImproperlyConfigured 异常。

如果你同时指定fieldsform_class 属性,将引发一个ImproperlyConfigured 异常。

  1. Changed in Django 1.8:
  2. 省略fields 属性在以前是允许的,但是导致表单带有模型的所有字段。
  1. Changed in Django 1.8:
  2. 以前,如果fields form_class 两个都指定,会默默地忽略 fields

最后,我我们来将这些新的视图放到URLconf 中:

  1. #urls.py
  2. from django.conf.urls import url
  3. from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete
  4. urlpatterns = [
  5. # ...
  6. url(r'author/add/$', AuthorCreate.as_view(), name='author_add'),
  7. url(r'author/(?P<pk>[0-9]+)/$', AuthorUpdate.as_view(), name='author_update'),
  8. url(r'author/(?P<pk>[0-9]+)/delete/$', AuthorDelete.as_view(), name='author_delete'),
  9. ]

这些表单继承SingleObjectTemplateResponseMixin,它使用template_name_suffix并基于模型来构造template_name

在这个例子中:

  • CreateViewUpdateView 使用 myapp/author_form.html
  • DeleteView 使用 myapp/author_confirm_delete.html

如果你希望分开CreateViewUpdateView 的模板,你可以设置你的视图类的template_nametemplate_name_suffix

模型和request.user

为了跟踪使用CreateView 创建一个对象的用户,你可以使用一个自定义的ModelForm 来实现这点。首先,向模型添加外键关联:

  1. #models.py
  2. from django.contrib.auth.models import User
  3. from django.db import models
  4. class Author(models.Model):
  5. name = models.CharField(max_length=200)
  6. created_by = models.ForeignKey(User)
  7. # ...

在这个视图中,请确保你没有将created_by 包含进要编辑的字段列表,并覆盖form_valid() 来添加这个用户:

  1. #views.py
  2. from django.views.generic.edit import CreateView
  3. from myapp.models import Author
  4. class AuthorCreate(CreateView):
  5. model = Author
  6. fields = ['name']
  7. def form_valid(self, form):
  8. form.instance.created_by = self.request.user
  9. return super(AuthorCreate, self).form_valid(form)

注意,你需要使用login_required() 来装饰这个视图,或者在form_valid() 中处理未认证的用户。

AJAX 示例

下面是一个简单的实例,展示你可以如何实现一个表单,使它可以同时为AJAX 请求和‘普通的’表单POST 工作:

  1. from django.http import JsonResponse
  2. from django.views.generic.edit import CreateView
  3. from myapp.models import Author
  4. class AjaxableResponseMixin(object):
  5. """
  6. Mixin to add AJAX support to a form.
  7. Must be used with an object-based FormView (e.g. CreateView)
  8. """
  9. def form_invalid(self, form):
  10. response = super(AjaxableResponseMixin, self).form_invalid(form)
  11. if self.request.is_ajax():
  12. return JsonResponse(form.errors, status=400)
  13. else:
  14. return response
  15. def form_valid(self, form):
  16. # We make sure to call the parent's form_valid() method because
  17. # it might do some processing (in the case of CreateView, it will
  18. # call form.save() for example).
  19. response = super(AjaxableResponseMixin, self).form_valid(form)
  20. if self.request.is_ajax():
  21. data = {
  22. 'pk': self.object.pk,
  23. }
  24. return JsonResponse(data)
  25. else:
  26. return response
  27. class AuthorCreate(AjaxableResponseMixin, CreateView):
  28. model = Author
  29. fields = ['name']

译者:Django 文档协作翻译小组,原文:Built-in editing views

本文以 CC BY-NC-SA 3.0 协议发布,转载请保留作者署名和文章出处。

Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。