Django 类视图学习笔记(四) 基于类视图处理表单


处理表单一般有以下三个步骤:

  1. 在request.method为GET时初始化表单并呈现在前端页面。
  2. 对POST方法中数据进行验证。
  3. 得到有效数据做进一步处理(通常是重定向)。

这些步骤通常会产生大量的重复代码,为了避免这种情况,Django为表单处理提供有特定的类视图。

一、基本表单处理

首先是forms.py:

from django import forms

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

    def send_email(self):
    	#使用合法的数据发送邮件
        pass

这里的视图用到FormView:

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):
    	#当合法数据被post时该方法被调用,并返回一个HttpResponse。
        form.send_email()
        return super().form_valid(form)

有两点需要说明:

  • FormView继承了TemplateResponseMixin因此在FormVIew中可以使用template_name。
  • form_valid()默认重定向到success_url。

二、使用ModelForm

如果类视图能够确定使用的模型类,那么它将自动创建ModelForm,以下方式都能提供这个功能:

  • 如果model属性被提供,那么类视图将使用该模型类。
  • 如果get_object()返回一个object,那么该object依赖的模型类将会被使用。
  • 如果queryset被提供,那么queryset依赖的模型类将会被使用。

与模型窗体有关的视图(CreateView、UpdateView、DeleteView)提供有form_valid()方法,它的功能是验证数据并自动保存模型,如果你有另外的需求,请重写它。

你甚至连success_url也不需要提供,前提是model类实现了 get_absolute_url() 方法。

另外,如果你不想让Django帮你选择 modelform 而是想自定义它,请使用form_class,但这时就又回退到普通表单处理的范畴了。

接下来看一个例子:

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

我们在model类中实现了get_absolute_url()方法,它会根据获得的model中的对象自动重定向。有了该方法,我们就不必在视图中定义success_url了。

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

这里要注意,我们的逻辑是当创建一个对象,或者更新一个对象时,自动重定向到该对象的详情界面,但删除该对象的话,因为对象被删除,因此get_absolute_url()就不管用了,所以我们要自定义success_url。

这里用reverse_lazy()实现,reverse_lazy()是reverse()的一个延迟版本,因为删除author后需要调用author-list,但这时可能url还没有加载到。

这里的fields属性与ModelForm中的Meta类的fields在功能上是一样的。

需要注意的是fields属性与form_class不能一起使用,因为一个表单不能既是普通表单又是ModelForm,如果你这么做了,那么Django将抛出 ImproperlyConfigured 错误。

最后,让我们看一下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。
虽然我们没有指定模板,这时Django会使用默认值,其中,CreateView和UpdateView默认使用myapp/author_form.html,DeleteView默认使用myapp/author_confirm_delete.html,如果你希望自己指定模板,请设置 template_name 或者 template_name_suffix 参数。


三、Models与Request.user

现在我们希望CreateView创建的author自动映射向User模型中的user,且该user必须是当前登录的用户,以表名该author为当前登录的user所创建,这该怎么做呢?

首先,修改我们的author模型。

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与User关联,在ModelForm中,注意不要把该外键添加到fields中,我们需要在数据验证时另外把created_by的值指向request.user,就跟我们用函数视图时的逻辑一样。

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会防止未登录的用户访问这里,如果你没有添加该Mixin,你也可以在form_valid()中添加逻辑去处理。


四、Ajax示例

下面是一个如何在类视图的表单处理中使用ajax的示例:

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

class AjaxableResponseMixin:
    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):
        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']
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章