クラスベースのビューでフォームを扱う¶
フォームの処理には、一般に3つの場合分けが存在します。
- 最初の GET リクエスト (空白またはデフォルト値が埋め込まれたフォームを返す)
- 無効なデータの POST リクエスト (よくあるパターンは、エラー表示を追加したフォームを再表示する)
- 有効なデータの POST リクエスト (データを処理し、普通はリダイレクトを行う)
これらの処理を自分で実装しようとすると、多くの場合、多数の繰り返しの定型コードを書くことになってしまいます (ビューでフォームを使う を参照)。これを避けるために、Django はフォームを処理するための一般的なクラスビューを用意しています。
基本的なフォーム¶
以下のようなコンタクトフォームがあったとします。
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
を使うことで構築できます。
from myapp.forms import ContactForm
from django.views.generic.edit import FormView
class ContactFormView(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
だったとしても、モデルを指定する必要があります。
最初に、Author
クラスに get_absolute_url()
を追加する必要があります:
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
およびその仲間たちを使って実際に動作させることができます。ここでは一般的なクラスベースのビューのみを設定している点が重要です; 自分自身でロジックを書く必要はありません:
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from myapp.models import Author
class AuthorCreateView(CreateView):
model = Author
fields = ["name"]
class AuthorUpdateView(UpdateView):
model = Author
fields = ["name"]
class AuthorDeleteView(DeleteView):
model = Author
success_url = reverse_lazy("author-list")
注釈
ファイルがインポートされるときに urls は読み込まれないので、 reverse_lazy()
を reverse()
の代わりに使う必要があります。
fields
属性は ModelForm
の内部 Meta
クラスの fields
属性と同じように動作します。別の方法でフォームクラスを定義しない限り、この属性は必須であり、属性がない場合、ビューは ImproperlyConfigured
例外を発生させます。
fields
属性と form_class
属性の両方を指定した場合、 ImproperlyConfigured
例外が発生します。
最後に、これらの新しいビューをURLconfにフックします:
from django.urls import path
from myapp.views import AuthorCreateView, AuthorDeleteView, AuthorUpdateView
urlpatterns = [
# ...
path("author/add/", AuthorCreateView.as_view(), name="author-add"),
path("author/<int:pk>/", AuthorUpdateView.as_view(), name="author-update"),
path("author/<int:pk>/delete/", AuthorDeleteView.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
を使います。まず、モデルに外部キー関係を追加します:
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()
をオーバーライドしてユーザーを追加します:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreateView(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()
で未承認ユーザを処理する必要があります。
コンテンツネゴシエーションの例¶
ここでは、API ベースのワークフローと「通常の」フォーム POST で動作するフォームの実装例を紹介します:
from django.http import JsonResponse
from django.views.generic.edit import CreateView
from myapp.models import Author
class JsonableResponseMixin:
"""
Mixin to add JSON 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.accepts("text/html"):
return response
else:
return JsonResponse(form.errors, status=400)
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.accepts("text/html"):
return response
else:
data = {
"pk": self.object.pk,
}
return JsonResponse(data)
class AuthorCreateView(JsonableResponseMixin, CreateView):
model = Author
fields = ["name"]