クラスベースのビューでフォームを扱うLink to this heading
フォームの処理には、一般に3つの場合分けが存在します。
最初の GET リクエスト (空白またはデフォルト値が埋め込まれたフォームを返す)
無効なデータの POST リクエスト (よくあるパターンは、エラー表示を追加したフォームを再表示する)
有効なデータの POST リクエスト (データを処理し、普通はリダイレクトを行う)
これらの処理を自分で実装しようとすると、多くの場合、多数の繰り返しのボイラープレートコードを書くことになってしまいます (Using a form in a view を参照)。これを避けられるように、Django はフォームを処理するための一般的なクラスビューを用意しています。
基本的なフォームLink to this heading
以下のような単純なコンタクトフォームがあったとします。
forms.pyfrom 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.pyfrom 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にリダイレクトするというものです。
モデルフォームLink to this heading
ジェネリックビューが輝きを見せるのは、モデルとともに使用した時です。ジェネリックビューは ModelForm を自動的に生成するので、どのモデルクラスを使うのかを選択することができます。
model属性が与えられた場合には、そのモデルクラスが使用されます。get_object()がオブジェクトを帰す場合には、そのオブジェクトのクラスが使用されます。querysetが与えられた場合には、そのqueryset に対するモデルが使用されます。
Model form views provide a
form_valid() implementation
that saves the model automatically. You can override this if you have any
special requirements; see below for examples.
You don't even need to provide a success_url for
CreateView or
UpdateView - they will use
get_absolute_url() on the model object if available.
If you want to use a custom ModelForm (for instance to
add extra validation) simply set
form_class on your view.
First we need to add get_absolute_url() to our
Author class:
models.pyfrom 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})
Then we can use CreateView and friends to do the actual
work. Notice how we're just configuring the generic class-based views
here; we don't have to write any logic ourselves:
views.pyfrom 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')
The fields attribute works the same way as the fields attribute on the
inner Meta class on ModelForm. Unless you define the
form class in another way, the attribute is required and the view will raise
an ImproperlyConfigured exception if it's not.
If you specify both the fields
and form_class attributes, an
ImproperlyConfigured exception will be raised.
Finally, we hook these new views into the URLconf:
urls.pyfrom 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'),
]
Models and request.userLink to this heading
To track the user that created an object using a CreateView,
you can use a custom ModelForm to do this. First, add
the foreign key relation to the model:
models.pyfrom 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)
# ...
In the view, ensure that you don't include created_by in the list of fields
to edit, and override
form_valid() to add the user:
views.pyfrom django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
Note that you'll need to decorate this
view using
login_required(), or
alternatively handle unauthorized users in the
form_valid().
AJAX の例Link to this heading
以下に挙げるのは、「普通の」フォームが POST するのと同様に AJAX リクエストで動作するフォームの簡単な実装例です。
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']