特定のモデルインスタンスに関する情報を表示する必要があるビューがあるので、DetailView
を使用します。 GET
でフォームを表示し、POST
でフォームを検証する通常のフォーム(モデルフォームではない)を処理するためにも、同じビューが必要です。そのために、私はFormView
を使用しようとしていますが、両方のビュークラスの組み合わせは機能しません。
class FooView(FormView, DetailView):
# configs here
GET
では(質問を簡単にするため、GET
には別の問題があるため、POST
の問題のみを示します)、フォームが追加されないため、機能しませんコンテキスト。その理由は、そのクラスのメソッド解決順序に関係しています。
>>> inspect.getmro(FooView)
(FooView,
Django.views.generic.edit.FormView,
Django.views.generic.detail.DetailView,
Django.views.generic.detail.SingleObjectTemplateResponseMixin,
Django.views.generic.base.TemplateResponseMixin,
Django.views.generic.edit.BaseFormView,
Django.views.generic.edit.FormMixin,
Django.views.generic.detail.BaseDetailView,
Django.views.generic.detail.SingleObjectMixin,
Django.views.generic.base.ContextMixin,
Django.views.generic.edit.ProcessFormView,
Django.views.generic.base.View,
object)
リクエスト内でDjangoはフォームを取得してコンテキストに追加する必要があります。これはProcessFormView.get
:
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates a blank version of the form.
"""
form_class = self.get_form_class()
form = self.get_form(form_class)
return self.render_to_response(self.get_context_data(form=form))
ただし、get
が定義されているMROの最初のクラスはBaseDetailView
です。
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
ご覧のとおり、BaseDetailView.get
がsuper
を呼び出すことはないため、ProcessFormView.get
は決して呼び出されないため、フォームはコンテキストに追加されません。これは、GET
とPOST
のこれらすべてのニュアンスを処理できるミックスインビューを作成することで修正できますが、クリーンなソリューションだとは思いません。
私の質問:ミックスインを作成せずに、DjangoのデフォルトのCBV実装で私が望むことを達成する方法はありますか?
1つの解決策は、上記の脚光のコメントに従って、ミックスインを使用することです。
別のアプローチは、2つの別個のビューを使用することです。1つはDetailView
で、もう1つはFormView
です。次に、前者のテンプレートで、後者で使用しているのと同じフォームを表示します。ただし、action
属性を空のままにしないでください。代わりに、FormView
のURLに設定してください。これに沿った何か(私がテストせずにこれを書いているので、エラーに注意してください):
views.py
:
class MyDetailView(DetailView):
model = MyModel
template_name = 'my_detail_view.html'
def get_context_data(self, **kwargs):
context = super(MyDetailView, self).get_context_data(**kwargs)
context['form'] = MyFormClass
return context
class MyFormView(FormView):
form_class = MyFormClass
success_url = 'go/here/if/all/works'
my_detail_view.html
:
<!-- some representation of the MyModel object -->
<form method="post" action="{% url "my_form_view_url" %}">
{{ form }}
</form>
urls.py
:
# ...
url('^my_model/(?P<pk>\d+)/$', MyDetailView.as_view(), name='my_detail_view_url'),
url('^my_form/$', require_POST(MyFormView.as_view()), name='my_form_view_url'),
# ...
require_POST
デコレータはオプションです。MyFormView
にGET
からアクセスできないようにし、フォームが送信されたときにのみ処理されるようにする場合です。
Djangoには、この問題に関するかなり長いドキュメントもあります。
彼らは2つの異なるビューを作成し、詳細ビューに投稿時のフォームビューを参照させることを勧めています。
私は現在、このハックが機能するかどうかを確認しています:
class MyDetailFormView(FormView, DetailView):
model = MyModel
form_class = MyFormClass
template_name = 'my_template.html'
def get_context_data(self, **kwargs):
context = super(MyDetailFormView, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def post(self, request, *args, **kwargs):
return FormView.post(self, request, *args, **kwargs)
FormMixinを使用
views.py
from Django.contrib.auth import get_user_model
from Django.core.urlresolvers import (
reverse_lazy
)
from Django.http import Http404
from Django.shortcuts import (
render,
redirect
)
from Django.views.generic import (
DetailView,
FormView,
)
from Django.views.generic.edit import FormMixin
from .forms import SendRequestForm
User = get_user_model()
class ViewProfile(FormMixin, DetailView):
model = User
context_object_name = 'profile'
template_name = 'htmls/view_profile.html'
form_class = SendRequestForm
def get_success_url(self):
return reverse_lazy('view-profile', kwargs={'pk': self.object.pk})
def get_object(self):
try:
my_object = User.objects.get(id=self.kwargs.get('pk'))
return my_object
except self.model.DoesNotExist:
raise Http404("No MyModel matches the given query.")
def get_context_data(self, *args, **kwargs):
context = super(ViewProfile, self).get_context_data(*args, **kwargs)
profile = self.get_object()
# form
context['form'] = self.get_form()
context['profile'] = profile
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
#put logic here
return super(ViewProfile, self).form_valid(form)
def form_invalid(self, form):
#put logic here
return super(ViewProfile, self).form_invalid(form)
forms.py
from Django import forms
class SendRequestForm(forms.Form):
request_type = forms.CharField()
def clean_request_type(self):
request_type = self.cleaned_data.get('request_type')
if 'something' not in request_type:
raise forms.ValidationError('Something must be in request_type field.')
return request_type
urls.py
urlpatterns = [
url(r'^view-profile/(?P<pk>\d+)', ViewProfile.as_view(), name='view-profile'),
]
テンプレート
username -{{object.username}}
id -{{object.id}}
<form action="{% url 'view-profile' object.id %}" method="POST">
{% csrf_token %}
{{form}}
<input type="submit" value="Send request">
</form>
Djangoライトバードの例では、ライブラリMCBVを使用して一般的なビューを混在させています。
私のガイドのチュートリアルでは、変更されたDjango=ジェネリックビューに基づくクラスベースビューのライブラリを使用します。ライブラリはMCBV(Mはモジュラーを表します)と呼ばれます。ジェネリックCBVとの主な違いは、複数の汎用ビューを簡単に組み合わせて一致させることが可能(例:ListViewとCreateView、DetailViewとUpdateViewなど)
ここで説明に従うことができます: helper-functions
そして、それを使用してFormViewとDetailView、または何でも混合します
コード: [〜#〜] mcbv [〜#〜]
古い投稿ですが、参考にしてください。
エレガントで再利用可能な方法の1つは、フォームにカスタム包含タグを使用することです。
templatetags/my_forms_tag.py
from Django import template
from ..forms import MyFormClass
register = template.Library()
@register.inclusion_tag('<app>\my_form.html')
def form_tag(*args, **kwargs):
my_form = MyFormClass()
return {'my_form ':my_form}
my_form.html
<form method="post" action="{% url "my_form_view_url" %}">
{{ form }}
</form>
投稿は、インクルードタグを配置したビューごとにFormViewによって取得されます。また、インクルードで渡したコンテキストを受け取ることができます。 my_form_tagをロードしてMyFormのビューを作成し、urls.pyにエントリを含めることを忘れないでください
私はModelFormsと次のようなものを使用してソリューションを実行しました:作成したDetailViewのメソッドget_context_dataで:
form = CommentForm(
instance=Comment(
school=self.object, user=self.request.user.profile
)
)
context['form'] = form
そして、私のFormViewは次のようでした:
class SchoolComment(FormView):
form_class = CommentForm
def get_success_url(self):
return resolve_url('schools:school-profile', self.kwargs.get('pk'))
def form_valid(self, form):
form.save()
return super(SchoolComment, self).form_valid(form)