2013-06-05 2 views
13

У меня есть представление, где мне нужно отображать информацию об определенном экземпляре модели, поэтому я использую DetailView. Мне также нужен тот же вид для обработки обычной формы (а не образцовой формы), отображающей форму на GET и проверяя ее на POST. Чтобы сделать это, я пытаюсь использовать FormView однако сочетание обоих зрения clases не работает:Django comb DetailView и 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) 

В запросе, Джанго должен получить форму и добавить его в контекст. Это происходит в 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)) 

Однако первый класс с MRO, который имеет get определен в 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 никогда не будет называться, следовательно, форма не будет добавлена ​​в контекст. Это можно исправить, создав представление mixin, в котором все эти нюансы для GET и POST можно позаботиться, но я не чувствую, что это чистое решение.

Мой вопрос: есть ли способ выполнить то, что я хочу, с реализацией CBV по умолчанию Django без создания каких-либо миксинов?

+2

Почему вы не хотите использовать микшины? https://docs.djangoproject.com/en/dev/topics/class-based-views/mixins/#using-formmixin-with-detailview –

+0

Это похоже на достаточно простую задачу, с которой должен справиться встроенный CBV. – miki725

ответ

21

Одним из решений было бы использовать mixins, как указано выше в комментариях.

Другой подход состоит в том, чтобы иметь два отдельных вида, один a DetailView, а другой a FormView. Затем в шаблоне для первого отобразится тот же самый вид, который вы используете в последнем, за исключением того, что вы не оставите атрибут action пустым - вместо этого установите его в URL-адрес для FormView. Что-то вдоль линий этого (пожалуйста, остерегайтесь любых ошибок, как я пишу это без какого-либо тестирования):

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 и хотите, чтобы он обрабатывался только при отправке формы.

+1

Это верное решение, однако я надеялся, что у меня нет чего-то тривиального в реализации CBV, который позволит мне это сделать. – miki725

+2

Боюсь, что вы этого не сделали - для чего нужны миксины. :-) –

1

В Джанго По примеру из lightbird, они используют библиотеку, MCBV, смешивать общие взгляды:

учебники Мой гид будет использовать библиотеку представлений на основе классов на основе модифицированных общих взглядов Django; библиотека называется MCBV (M означает модульный) и основное отличие по сравнению с родовым CBVS является то, что можно смешивать и сочетать несколько общих взгляды легко (например, ListView и CreateView, DetailView и UpdateView и т.д.)

Вы можете следить за объяснение здесь: helper-functions

и использовать его для смешивания FormView и DetailView, или что

Код: MCBV

8

Django также имеет довольно длинную документацию по этому поводу проблема.

https://docs.djangoproject.com/en/1.8/topics/class-based-views/mixins/#using-formmixin-with-detailview

Они советуют сделать 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) 
+1

Это работает? – iMitwe

0

Я выполнил свое решение, используя ModelForms и что-то вроде этого: по методе get_context_data моего DetailView я сделал:

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

Используя 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> 
Смежные вопросы