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