2013-06-05 7 views
13

Ich habe eine Ansicht, wo ich Informationen über eine bestimmte Modellinstanz anzeigen muss, daher verwende ich eine DetailView. Ich brauche auch dieselbe Ansicht, um ein normales Formular (kein Modellformular) zu behandeln, beide zeigen das Formular unter GET an und validieren es unter POST. Um dies zu tun, ich versuche, ein FormView zu verwenden, aber die Kombination beide Ansicht clases nicht funktioniert:Django kombinieren Detailansicht und FormView

class FooView(FormView, DetailView): 
    # configs here 

In GET (zur Vereinfachung der Frage zeige ich nur das Problem mit GET seit POST hat eine andere Problem), es funktioniert nicht, da das Formular nie zum Kontext hinzugefügt wird. Der Grund hat mit der Methode Auflösung, um für diese Klasse zu tun:

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

Innerhalb der Anfrage hat Django die Form zu erhalten und es auf den Kontext hinzufügen. Das geschieht in 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)) 

jedoch die erste Klasse mit dem MRO die get hat definiert ist 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) 

Wie Sie die BaseDetailView.get nie super Anrufe sehen daher die ProcessFormView.get nie daher aufgerufen werden Das Formular wird nicht zum Kontext hinzugefügt. Dies kann durch Erstellen einer Mischansicht behoben werden, in der all diese Nuancen für GET und POST berücksichtigt werden können, aber ich halte es nicht für eine saubere Lösung.

Meine Frage: Gibt es irgendeinen Weg, um das zu erreichen, was ich mit Djangos Standard-CBV-Implementierung erreichen möchte, ohne irgendwelche Mixins zu erstellen?

+2

Warum möchten Sie keine Mixins verwenden? https://docs.djangoproject.com/en/dev/topics/class-based-views/mixins/#using-formmixin-with-detailview –

+0

Dies scheint wie eine einfache Aufgabe, die Build-in CBV sollte behandeln. – miki725

Antwort

21

Eine Lösung wäre die Verwendung von Mixins, wie in den obigen Ausführungen der Rampenlicht erwähnt.

Ein anderer Ansatz besteht darin, zwei separate Ansichten zu haben, eine DetailView und die andere eine FormView. Zeigen Sie dann in der Vorlage für die erstere dasselbe Formular an, das Sie in letzterer verwenden, mit der Ausnahme, dass Sie das Attribut action nicht leer lassen - setzen Sie es stattdessen auf die URL für die FormView. Etwas nach dem Vorbild dieser (bitte etwaige Fehler passen, wie ich dies ohne Prüfung hier schreibe):

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

In my_detail_view.html:

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

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

{{ form }} 

</form> 

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

Beachten Sie, dass die require_POST Decorator ist optional, falls Sie nicht möchten, dass die MyFormView von GET zugänglich ist und nur bei der Übergabe des Formulars verarbeitet werden soll.

+1

Dies ist eine gültige Lösung, aber ich hatte gehofft, dass ich etwas Triviales in der eingebauten CBV-Implementierung verpasse, die mir erlauben wird, das zu tun. – miki725

+2

Ich fürchte du bist nicht - dafür gibt es Mixins. :-) –

1

In Django By Example aus lightbird, verwenden sie eine Bibliothek, MCBV, allgemeine Ansichten zu mischen:

Tutorials Mein Führer ist eine Bibliothek von klassenbasierten Ansichten auf Basis modifizierter Django allgemeine Ansichten verwenden; Die Bibliothek wird als MCBV (M für modulares steht) und der Hauptunterschied im Vergleich zu generischem CBVS ist, dass es möglich ist, auf einfache Weise mehr allgemeinen Ansichten zu mischen und anzupassen (zB Listview und Create, Detailview und UpdateView etc.)

Sie können die Erklärung folgen hier: helper-functions

und es verwenden, Formview und Detailview zu mischen, oder was auch immer

Code: MCBV

8

Django hat auch eine ziemlich lange Dokumentation über diese Problem.

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

Sie beraten 2 verschiedene Ansichten zu machen, und haben die Detailansicht auf Post an die Formularansicht verweisen.

Ich sehe zur Zeit, wenn dieser Hack funktionieren könnte:

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

Funktioniert es? – iMitwe

0

ich meine Lösung unter Verwendung von ModelForms und so etwas wie diese: Auf Methode get_context_data meine Detailview ich gemacht:

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

Und mein FormView war wie:

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

von 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'), 
] 

Vorlage

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