Django Class Based View Composite
I'm using Django 1.3's Class-base generic views for a project. They're really nice but I would like to be DRYer. I have a page that displays a list of press coverage we've received and another which开发者_Go百科 displays a list of articles we've published. On the overview page I need to display both lists. I would like to create a composite view which accepts both views and creates a context with both querysets attached.
Kenzic was able to accomplish this by doing the following:
composite.py:
from django.views.generic.base import TemplateResponseMixin, View
class BaseCompositeView(TemplateResponseMixin, View):
composite_views = []
def get_composite_views(self):
return self.composite_views
def get_context_data(self, request, *args, **kwargs):
context = {}
composite_views = self.get_composite_views()
for composite_view in composite_views:
cls = composite_view[0]
try:
clsview = cls.as_view(**composite_view[1])
except IndexError:
clsview = cls.as_view()
view = clsview(request, *args, **kwargs)
context_data = view.context_data
context.update(context_data)
return context
def get(self, request, *args, **kwargs):
context = self.get_context_data(request, *args, **kwargs)
return self.render_to_response(context)
views.py:
from django.views.generic import TemplateView, ListView, DetailView
from composite import BaseCompositeView
from .models import *
class MediaCoverageList(ListView):
queryset = MediaCoverageItem.objects.order_by('-date')
class PressKitList(ListView):
queryset = PressKit.objects.all()
class NewsroomLanding(BaseCompositeView):
template_name = 'newsroom/landing.html'
composite_views = [
(MediaCoverageList,{
'paginate_by': 10,
},),
(PressKitList,)
]
In my mind, a view is simply one page. A view can have several forms, which I think is a better solution in your example.
Just split the template into several files, where each form have a small template, and use the template include directive to stitch it together. This also has the added advantage that the form can be reused in other views very simple.
Your solution is basically like frames but on the server instead of in the browser.
The simple way:
Don't use a generic view and the paginator object manually on both queryset.
It won't be that long, and it's not some lines that are going to kill your DRY IMO.
The generic but long way:
Create a view that wrap the __init_
_ and get_context
methods to pass argument to two ListView instances.
init should instanciate both ListView
, pass the parameters and add the instances to attributes of the wrapper.
get_context
should call the both get_context Listview instances method, and merge them into one context object.
Be sure to setup a different template_object_name
for each ListView
so they don't override each other in the context dict.
Do that in a generic way, and don't forget to pusblish you code on djangosnippet :-)
May be you can override the get_context_data method to add additional data to the context?
def get_context_data(self, **kwargs):
context = super(AuthorListView, self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['press_list'] = Press.objects.all()
context['articles_list] = Article.objects.all()
return context
and in your template you can get the press list and the article list using {{ press_list }}
and {{ articles_list }}
.
精彩评论