Fixed #16074 -- Added ContextMixin to class-based generic views to handle get_context_data. Thanks emyller, Luke Plant, Preston Holmes for working on the ticket and patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17875 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Claude Paroz 2012-04-06 21:24:33 +00:00
parent b4a9827133
commit 8663bc1103
9 changed files with 77 additions and 38 deletions

View File

@ -8,6 +8,16 @@ from django.utils.decorators import classonlymethod
logger = getLogger('django.request')
class ContextMixin(object):
"""
A default context mixin that passes the keyword arguments received by
get_context_data as the template context.
"""
def get_context_data(self, **kwargs):
return kwargs
class View(object):
"""
Intentionally simple parent class for all views. Only implements
@ -110,17 +120,13 @@ class TemplateResponseMixin(object):
return [self.template_name]
class TemplateView(TemplateResponseMixin, View):
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
A view that renders a template.
A view that renders a template. This view is different from all the others
insofar as it also passes ``kwargs`` as ``params`` to the template context.
"""
def get_context_data(self, **kwargs):
return {
'params': kwargs
}
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context = self.get_context_data(params=kwargs)
return self.render_to_response(context)

View File

@ -217,16 +217,6 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
return date_list
def get_context_data(self, **kwargs):
"""
Get the context. Must return a Context (or subclass) instance.
"""
items = kwargs.pop('object_list')
context = super(BaseDateListView, self).get_context_data(object_list=items)
context.update(kwargs)
return context
class BaseArchiveIndexView(BaseDateListView):
"""
Base class for archives of date-based items.

View File

@ -2,10 +2,10 @@ from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.http import Http404
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
class SingleObjectMixin(object):
class SingleObjectMixin(ContextMixin):
"""
Provides the ability to retrieve a single object for further manipulation.
"""
@ -86,11 +86,12 @@ class SingleObjectMixin(object):
return None
def get_context_data(self, **kwargs):
context = kwargs
context = {}
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
return context
context.update(kwargs)
return super(SingleObjectMixin, self).get_context_data(**context)
class BaseDetailView(SingleObjectMixin, View):

View File

@ -1,12 +1,12 @@
from django.forms import models as model_forms
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponseRedirect
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
from django.views.generic.detail import (SingleObjectMixin,
SingleObjectTemplateResponseMixin, BaseDetailView)
class FormMixin(object):
class FormMixin(ContextMixin):
"""
A mixin that provides a way to show and handle a form in a request.
"""
@ -45,9 +45,6 @@ class FormMixin(object):
})
return kwargs
def get_context_data(self, **kwargs):
return kwargs
def get_success_url(self):
if self.success_url:
url = self.success_url
@ -113,13 +110,14 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
return super(ModelFormMixin, self).form_valid(form)
def get_context_data(self, **kwargs):
context = kwargs
context = {}
if self.object:
context['object'] = self.object
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
return context
context.update(kwargs)
return super(ModelFormMixin, self).get_context_data(**context)
class ProcessFormView(View):

View File

@ -3,10 +3,10 @@ from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
class MultipleObjectMixin(object):
class MultipleObjectMixin(ContextMixin):
allow_empty = True
queryset = None
model = None
@ -103,10 +103,10 @@ class MultipleObjectMixin(object):
'is_paginated': False,
'object_list': queryset
}
context.update(kwargs)
if context_object_name is not None:
context[context_object_name] = queryset
return context
context.update(kwargs)
return super(MultipleObjectMixin, self).get_context_data(**context)
class BaseListView(MultipleObjectMixin, View):

View File

@ -270,6 +270,16 @@ more::
context['book_list'] = Book.objects.all()
return context
.. note::
Generally, get_context_data will merge the context data of all parent classes
with those of the current class. To preserve this behavior in your own classes
where you want to alter the context, you should be sure to call
get_context_data on the super class. When no two classes try to define the same
key, this will give the expected results. However if any class attempts to
override a key after parent classes have set it (after the call to super), any
children of that class will also need to explictly set it after super if they
want to be sure to override all parents.
Viewing subsets of objects
--------------------------

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import
import time
from django.core.exceptions import ImproperlyConfigured
@ -6,6 +8,7 @@ from django.test import TestCase, RequestFactory
from django.utils import unittest
from django.views.generic import View, TemplateView, RedirectView
from . import views
class SimpleView(View):
"""
@ -331,3 +334,19 @@ class RedirectViewTest(unittest.TestCase):
# we can't use self.rf.get because it always sets QUERY_STRING
response = RedirectView.as_view(url='/bar/')(self.rf.request(PATH_INFO='/foo/'))
self.assertEqual(response.status_code, 301)
class GetContextDataTest(unittest.TestCase):
def test_get_context_data_super(self):
test_view = views.CustomContextView()
context = test_view.get_context_data(kwarg_test='kwarg_value')
# the test_name key is inserted by the test classes parent
self.assertTrue('test_name' in context)
self.assertEqual(context['kwarg_test'], 'kwarg_value')
self.assertEqual(context['custom_key'], 'custom_value')
# test that kwarg overrides values assigned higher up
context = test_view.get_context_data(test_name='test_value')
self.assertEqual(context['test_name'], 'test_value')

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import
from .base import ViewTest, TemplateViewTest, RedirectViewTest
from .base import (ViewTest, TemplateViewTest, RedirectViewTest,
GetContextDataTest)
from .dates import (ArchiveIndexViewTests, YearArchiveViewTests,
MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests,
DateDetailViewTests)

View File

@ -14,10 +14,9 @@ class CustomTemplateView(generic.TemplateView):
template_name = 'generic_views/about.html'
def get_context_data(self, **kwargs):
return {
'params': kwargs,
'key': 'value'
}
context = super(CustomTemplateView, self).get_context_data(**kwargs)
context.update({'key': 'value'})
return context
class ObjectDetail(generic.DetailView):
@ -184,3 +183,18 @@ class BookDetailGetObjectCustomQueryset(BookDetail):
def get_object(self, queryset=None):
return super(BookDetailGetObjectCustomQueryset,self).get_object(
queryset=Book.objects.filter(pk=2))
class CustomContextView(generic.detail.SingleObjectMixin, generic.View):
model = Book
object = Book(name='dummy')
def get_object(self):
return Book(name="dummy")
def get_context_data(self, **kwargs):
context = {'custom_key': 'custom_value'}
context.update(kwargs)
return super(CustomContextView, self).get_context_data(**context)
def get_context_object_name(self, obj):
return "test_name"