# Django 2.2.9.dev20191202104323 文档

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

表单处理通常有3个途径：

- 初始 GET （空白或预填充表单）
- 带有合法数据的POST（通常是带有错误的重新显示的表单）
- 带有合法数据的POST（处理数据和通常重定向）

自己实现这个会导致很多重复的样板代码（查看 Using a form in a view ）。为了避免这个问题，Django 提供了一组通用基于类的视图来处理表单。

## 基础表单

提供一个简单的联系表单：

forms.py

    from django import forms

    class ContactForm(forms.Form):
        name = forms.CharField()
        message = forms.CharField(widget=forms.Textarea)

        def send_email(self):
            # send email using the self.cleaned_data dictionary
            pass

可以使用 `FormView` 构建视图：

views.py

    from myapp.forms import ContactForm
    from django.views.generic.edit import FormView

    class ContactView(FormView):
        template_name = 'contact.html'
        form_class = ContactForm
        success_url = '/thanks/'

        def form_valid(self, form):
            # This method is called when valid form data has been POSTed.
            # It should return an HttpResponse.
            form.send_email()
            return super().form_valid(form)

注意：

- FormView 继承了 `TemplateResponseMixin` ，因此可以在这里使用 `template_name` 。
- 默认实现了 `form_valid()` 简单重定向至 `success_url` 。

## 模型表单

通用视图在模型一起工作时真的很赞。这些通用视图将自动创建 `ModelForm` ，只要他们能找出要使用的模型类：

- 如果已经给出了 `model` 属性，则使用这个模型类。
- 如果 `get_object()` 返回一个对象，则使用这个对象的类。
- 如果已经给出了 `queryset` ，则使用这个查询集的模型。

模型表单视图提供一个 `form_valid()` 实现，来自动保存模型。如果你有特别的需求，你可以覆盖它。下面是例子。

你甚至不需要为 `CreateView` 或 `UpdateView` 提供 `success_url` 。如果可用，它们将在模型对象上使用 `get_absolute_url()` 。

如果你想使用一个自定义的 `ModelForm` （比如添加额外的验证），用来在视图上简单设置 `form_class` 。

注解

当指定一个自定义表单类时，必须也要指定模型，即使 `form_class` 可能是一个 `ModelForm` 。

首先我们需要添加 `get_absolute_url()` 到 `Author` 类：

models.py

    from django.db import models
    from django.urls import reverse

    class Author(models.Model):
        name = models.CharField(max_length=200)

        def get_absolute_url(self):
            return reverse('author-detail', kwargs={'pk': self.pk})

然后可以使用 `CreateView` 并友好的执行实际工作。注意这里我们如何配置通用基于类的视图。我们不用自己编写任何逻辑：

views.py

    from django.urls import reverse_lazy
    from django.views.generic.edit import CreateView, DeleteView, UpdateView
    from myapp.models import Author

    class AuthorCreate(CreateView):
        model = Author
        fields = ['name']

    class AuthorUpdate(UpdateView):
        model = Author
        fields = ['name']

    class AuthorDelete(DeleteView):
        model = Author
        success_url = reverse_lazy('author-list')

注解

我们必须在这里使用 `reverse_lazy()` ，而不是 `reverse()` ，因为在文件导入时不加载 urls 。

`fields` 属性的工作方式同 `ModelForm` 中内部 `Meta` 类的 `fields` 属性一样。除非你使用其他方式定义表单类，该属性是必需的，如果属性不存在，视图将引发 `ImproperlyConfigured` 异常。

如果同时指定了 `fields` 和 `form_class` 属性，将会引发 `ImproperlyConfigured` 异常。

最后将这些新视图挂钩到URLconf中：

urls.py

    from django.urls import path
    from myapp.views import AuthorCreate, AuthorDelete, AuthorUpdate

    urlpatterns = [
        # ...
        path('author/add/', AuthorCreate.as_view(), name='author-add'),
        path('author/<int:pk>/', AuthorUpdate.as_view(), name='author-update'),
        path('author/<int:pk>/delete/', AuthorDelete.as_view(), name='author-delete'),
    ]

注解

这些视图继承 `SingleObjectTemplateResponseMixin` ，它使用 `template_name_suffix` 来构建基于模型的 `template_name` 。

在这个例子里：

- `CreateView` 和 `UpdateView` 使用 `myapp/author_form.html` 。
- `DeleteView` 使用 `myapp/author_confirm_delete.html` 。

如果你想为 `CreateView` 和 `UpdateView` 制作单独的模板，你可以在视图类上设置 `template_name` 或 `template_name_suffix` 。

## 模型和 `request.user`

若要跟踪使用 `CreateView` 创建的用户，你可以使用自定义的 `ModelForm` 来执行此操作。首先，在模型里添加外键关系：

models.py

    from django.contrib.auth.models import User
    from django.db import models

    class Author(models.Model):
        name = models.CharField(max_length=200)
        created_by = models.ForeignKey(User, on_delete=models.CASCADE)

        # ...

在这个视图中，确保你没有在要编辑的字段列表中包含 `created_by` 字段 ，并且要覆盖 `form_valid()` 来添加用户：

views.py

    from django.contrib.auth.mixins import LoginRequiredMixin
    from django.views.generic.edit import CreateView
    from myapp.models import Author

    class AuthorCreate(LoginRequiredMixin, CreateView):
        model = Author
        fields = ['name']

        def form_valid(self, form):
            form.instance.created_by = self.request.user
            return super().form_valid(form)

`LoginRequiredMixin` 防止那些未登录的用户访问表单。如果忽略，那么你将需要在 `form_valid()` 里处理未授权的用户。

## AJAX 案例

下面是一个展示了如何实现适用于 AJAX 请求的表单以及普通表单的 POST 请求：

    from django.http import JsonResponse
    from django.views.generic.edit import CreateView
    from myapp.models import Author

    class AjaxableResponseMixin:
        """
        Mixin to add AJAX support to a form.
        Must be used with an object-based FormView (e.g. CreateView)
        """
        def form_invalid(self, form):
            response = super().form_invalid(form)
            if self.request.is_ajax():
                return JsonResponse(form.errors, status=400)
            else:
                return response

        def form_valid(self, form):
            # We make sure to call the parent's form_valid() method because
            # it might do some processing (in the case of CreateView, it will
            # call form.save() for example).
            response = super().form_valid(form)
            if self.request.is_ajax():
                data = {
                    'pk': self.object.pk,
                }
                return JsonResponse(data)
            else:
                return response

    class AuthorCreate(AjaxableResponseMixin, CreateView):
        model = Author
        fields = ['name']