2013-06-05 12 views
13

Mam widok, w którym muszę wyświetlić informacje o określonym wystąpieniu modelu, dlatego używam DetailView. Też potrzebuję tego samego widoku, aby obsłużyć zwykły formularz (nie formularz modelu), oba wyświetlając formularz na GET i potwierdzając go na POST. Aby to zrobić, próbuję użyć FormView jednak połączenie obu widoków clases nie działa:Django łączą DetailView i FormView

class FooView(FormView, DetailView): 
    # configs here 

W GET (dla uproszczenia kwestii pokażę tylko problem z GET od POST ma inny problem), nie działa, ponieważ formularz nigdy nie zostanie dodany do kontekstu. Powodem ma do czynienia z celem rezolucji metoda dla tej klasy:

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

w żądaniu, Django ma dostać formularz i dodać go do kontekstu. Co dzieje się w 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)) 

Jednak pierwsza klasa z MRO, który ma get zdefiniowany jest 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) 

Jak widać BaseDetailView.get nigdy nazywa super stąd ProcessFormView.get nigdy nie zostanie wywołana stąd formularz nie zostanie dodany do kontekstu. Można to naprawić, tworząc widok mixin, w którym wszystkie te niuanse mogą być zajęte, jednak nie uważam, że jest to czyste rozwiązanie.

Moje pytanie: czy istnieje sposób na osiągnięcie tego, co chcę z domyślną implementacją CBV Django bez tworzenia żadnych miksów?

+2

Dlaczego nie chcesz używać miksów? https://docs.djangoproject.com/en/dev/topics/class-based-views/mixins/#using-formmixin-with-detailview –

+0

Wydaje się, że jest to dość proste zadanie, które powinna obsłużyć wbudowana funkcja CBV. – miki725

Odpowiedz

21

Jednym z rozwiązań byłoby użycie mixin, zgodnie z komentarzem limelight powyżej.

Innym podejściem jest posiadanie dwóch osobnych widoków, jednego a DetailView, a drugiego FormView. Następnie, w szablonie dla pierwszego, wyświetl tę samą formę, której używasz w drugiej z nich, z tym wyjątkiem, że nie pozostawisz pustego atrybutu action - zamiast tego ustaw go na adres URL dla FormView. Coś na wzór tego (należy wystrzegać się wszelkich błędów, jak piszę to bez badania):

W 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' 

W my_detail_view.html:

<!-- some representation of the MyModel object --> 

<form method="post" action="{% url "my_form_view_url" %}"> 

{{ form }} 

</form> 

W 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'), 
# ... 

Pamiętaj, że require_POST dekorator jest opcjonalny, jeśli użytkownik nie chce, aby MyFormView był dostępny przez i chciał, aby był on przetwarzany tylko po przesłaniu formularza.

+1

To jest poprawne rozwiązanie, ale miałem nadzieję, że brakuje mi czegoś banalnego w wbudowanej implementacji CBV, która pozwoli mi to zrobić. – miki725

+2

Obawiam się, że nie jesteś - po to są mixiny. :-) –

1

W Django Według przykładu z lightbird, są one przy użyciu biblioteki, MCBV, wymieszać rodzajowe widoki:

tutoriale moim przewodnikiem będzie korzystać z biblioteki poglądów klasowych opartych na bazie modyfikowanych Django ogólnych poglądów; biblioteka nazywa MCBV (M oznacza modułowych) i główna różnica w porównaniu do CBVs generycznych jest to, że można mieszać i łatwo dopasować wiele widoków rodzajowe (np ListView i CreateView, DetailView i UpdateView, etc.)

Możesz śledzić wyjaśnienie tutaj: helper-functions

i używać go mieszać FormView i DetailView, czy cokolwiek

Kod: MCBV

8

Django ma również dość długie dokumentację na ten temat problem.

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

Radzą, aby 2 różne poglądy i mają widok szczegółowo odnoszą się do widoku formularza na róg.

Jestem obecnie zobaczyć, czy to może działać Hack:

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

Czy to działa? – iMitwe

0

występowałem moje rozwiązanie używając ModelForms i coś takiego: na metodzie get_context_data mojego DetailView zrobiłem:

form = CommentForm(
     instance=Comment(
      school=self.object, user=self.request.user.profile 
     ) 
    ) 
    context['form'] = form 

a moja FormView było jak:

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

Korzystając 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'), 
] 

szablon

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> 
Powiązane problemy