Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #16074 -- Added ContextMixin to class-based generic views to ha…

…ndle 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
  • Loading branch information...
commit 8663bc110305844b2f8b0829ee2ddfc5be61b758 1 parent b4a9827
@claudep claudep authored
View
22 django/views/generic/base.py
@@ -8,6 +8,16 @@
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 @@ def get_template_names(self):
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
10 django/views/generic/dates.py
@@ -217,16 +217,6 @@ def get_date_list(self, queryset, date_type):
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
9 django/views/generic/detail.py
@@ -2,10 +2,10 @@
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 @@ def get_context_object_name(self, obj):
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
12 django/views/generic/edit.py
@@ -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 @@ def get_form_kwargs(self):
})
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 @@ def form_valid(self, form):
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
8 django/views/generic/list.py
@@ -3,10 +3,10 @@
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 @@ def get_context_data(self, **kwargs):
'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
10 docs/topics/class-based-views.txt
@@ -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
19 tests/regressiontests/generic_views/base.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
import time
from django.core.exceptions import ImproperlyConfigured
@@ -6,6 +8,7 @@
from django.utils import unittest
from django.views.generic import View, TemplateView, RedirectView
+from . import views
class SimpleView(View):
"""
@@ -331,3 +334,19 @@ def test_redirect_when_meta_contains_no_query_string(self):
# 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
3  tests/regressiontests/generic_views/tests.py
@@ -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
22 tests/regressiontests/generic_views/views.py
@@ -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"
Please sign in to comment.
Something went wrong with that request. Please try again.