Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
Claude Paroz authored April 06, 2012
22  django/views/generic/base.py
@@ -8,6 +8,16 @@
8 8
 logger = getLogger('django.request')
9 9
 
10 10
 
  11
+class ContextMixin(object):
  12
+    """
  13
+    A default context mixin that passes the keyword arguments received by
  14
+    get_context_data as the template context.
  15
+    """
  16
+
  17
+    def get_context_data(self, **kwargs):
  18
+        return kwargs
  19
+
  20
+
11 21
 class View(object):
12 22
     """
13 23
     Intentionally simple parent class for all views. Only implements
@@ -110,17 +120,13 @@ def get_template_names(self):
110 120
             return [self.template_name]
111 121
 
112 122
 
113  
-class TemplateView(TemplateResponseMixin, View):
  123
+class TemplateView(TemplateResponseMixin, ContextMixin, View):
114 124
     """
115  
-    A view that renders a template.
  125
+    A view that renders a template.  This view is different from all the others
  126
+    insofar as it also passes ``kwargs`` as ``params`` to the template context.
116 127
     """
117  
-    def get_context_data(self, **kwargs):
118  
-        return {
119  
-            'params': kwargs
120  
-        }
121  
-
122 128
     def get(self, request, *args, **kwargs):
123  
-        context = self.get_context_data(**kwargs)
  129
+        context = self.get_context_data(params=kwargs)
124 130
         return self.render_to_response(context)
125 131
 
126 132
 
10  django/views/generic/dates.py
@@ -217,16 +217,6 @@ def get_date_list(self, queryset, date_type):
217 217
 
218 218
         return date_list
219 219
 
220  
-    def get_context_data(self, **kwargs):
221  
-        """
222  
-        Get the context. Must return a Context (or subclass) instance.
223  
-        """
224  
-        items = kwargs.pop('object_list')
225  
-        context = super(BaseDateListView, self).get_context_data(object_list=items)
226  
-        context.update(kwargs)
227  
-        return context
228  
-
229  
-
230 220
 class BaseArchiveIndexView(BaseDateListView):
231 221
     """
232 222
     Base class for archives of date-based items.
9  django/views/generic/detail.py
@@ -2,10 +2,10 @@
2 2
 from django.http import Http404
3 3
 from django.utils.encoding import smart_str
4 4
 from django.utils.translation import ugettext as _
5  
-from django.views.generic.base import TemplateResponseMixin, View
  5
+from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
6 6
 
7 7
 
8  
-class SingleObjectMixin(object):
  8
+class SingleObjectMixin(ContextMixin):
9 9
     """
10 10
     Provides the ability to retrieve a single object for further manipulation.
11 11
     """
@@ -86,11 +86,12 @@ def get_context_object_name(self, obj):
86 86
             return None
87 87
 
88 88
     def get_context_data(self, **kwargs):
89  
-        context = kwargs
  89
+        context = {}
90 90
         context_object_name = self.get_context_object_name(self.object)
91 91
         if context_object_name:
92 92
             context[context_object_name] = self.object
93  
-        return context
  93
+        context.update(kwargs)
  94
+        return super(SingleObjectMixin, self).get_context_data(**context)
94 95
 
95 96
 
96 97
 class BaseDetailView(SingleObjectMixin, View):
12  django/views/generic/edit.py
... ...
@@ -1,12 +1,12 @@
1 1
 from django.forms import models as model_forms
2 2
 from django.core.exceptions import ImproperlyConfigured
3 3
 from django.http import HttpResponseRedirect
4  
-from django.views.generic.base import TemplateResponseMixin, View
  4
+from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
5 5
 from django.views.generic.detail import (SingleObjectMixin,
6 6
                         SingleObjectTemplateResponseMixin, BaseDetailView)
7 7
 
8 8
 
9  
-class FormMixin(object):
  9
+class FormMixin(ContextMixin):
10 10
     """
11 11
     A mixin that provides a way to show and handle a form in a request.
12 12
     """
@@ -45,9 +45,6 @@ def get_form_kwargs(self):
45 45
             })
46 46
         return kwargs
47 47
 
48  
-    def get_context_data(self, **kwargs):
49  
-        return kwargs
50  
-
51 48
     def get_success_url(self):
52 49
         if self.success_url:
53 50
             url = self.success_url
@@ -113,13 +110,14 @@ def form_valid(self, form):
113 110
         return super(ModelFormMixin, self).form_valid(form)
114 111
 
115 112
     def get_context_data(self, **kwargs):
116  
-        context = kwargs
  113
+        context = {}
117 114
         if self.object:
118 115
             context['object'] = self.object
119 116
             context_object_name = self.get_context_object_name(self.object)
120 117
             if context_object_name:
121 118
                 context[context_object_name] = self.object
122  
-        return context
  119
+        context.update(kwargs)
  120
+        return super(ModelFormMixin, self).get_context_data(**context)
123 121
 
124 122
 
125 123
 class ProcessFormView(View):
8  django/views/generic/list.py
@@ -3,10 +3,10 @@
3 3
 from django.http import Http404
4 4
 from django.utils.encoding import smart_str
5 5
 from django.utils.translation import ugettext as _
6  
-from django.views.generic.base import TemplateResponseMixin, View
  6
+from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
7 7
 
8 8
 
9  
-class MultipleObjectMixin(object):
  9
+class MultipleObjectMixin(ContextMixin):
10 10
     allow_empty = True
11 11
     queryset = None
12 12
     model = None
@@ -103,10 +103,10 @@ def get_context_data(self, **kwargs):
103 103
                 'is_paginated': False,
104 104
                 'object_list': queryset
105 105
             }
106  
-        context.update(kwargs)
107 106
         if context_object_name is not None:
108 107
             context[context_object_name] = queryset
109  
-        return context
  108
+        context.update(kwargs)
  109
+        return super(MultipleObjectMixin, self).get_context_data(**context)
110 110
 
111 111
 
112 112
 class BaseListView(MultipleObjectMixin, View):
10  docs/topics/class-based-views.txt
@@ -270,6 +270,16 @@ more::
270 270
             context['book_list'] = Book.objects.all()
271 271
             return context
272 272
 
  273
+.. note::
  274
+
  275
+    Generally, get_context_data will merge the context data of all parent classes
  276
+    with those of the current class.  To preserve this behavior in your own classes
  277
+    where you want to alter the context, you should be sure to call
  278
+    get_context_data on the super class. When no two classes try to define the same
  279
+    key, this will give the expected results. However if any class attempts to
  280
+    override a key after parent classes have set it (after the call to super), any
  281
+    children of that class will also need to explictly set it after super if they
  282
+    want to be sure to override all parents.
273 283
 
274 284
 Viewing subsets of objects
275 285
 --------------------------
19  tests/regressiontests/generic_views/base.py
... ...
@@ -1,3 +1,5 @@
  1
+from __future__ import absolute_import
  2
+
1 3
 import time
2 4
 
3 5
 from django.core.exceptions import ImproperlyConfigured
@@ -6,6 +8,7 @@
6 8
 from django.utils import unittest
7 9
 from django.views.generic import View, TemplateView, RedirectView
8 10
 
  11
+from . import views
9 12
 
10 13
 class SimpleView(View):
11 14
     """
@@ -331,3 +334,19 @@ def test_redirect_when_meta_contains_no_query_string(self):
331 334
         # we can't use self.rf.get because it always sets QUERY_STRING
332 335
         response = RedirectView.as_view(url='/bar/')(self.rf.request(PATH_INFO='/foo/'))
333 336
         self.assertEqual(response.status_code, 301)
  337
+
  338
+
  339
+class GetContextDataTest(unittest.TestCase):
  340
+
  341
+    def test_get_context_data_super(self):
  342
+        test_view = views.CustomContextView()
  343
+        context = test_view.get_context_data(kwarg_test='kwarg_value')
  344
+
  345
+        # the test_name key is inserted by the test classes parent
  346
+        self.assertTrue('test_name' in context)
  347
+        self.assertEqual(context['kwarg_test'], 'kwarg_value')
  348
+        self.assertEqual(context['custom_key'], 'custom_value')
  349
+
  350
+        # test that kwarg overrides values assigned higher up
  351
+        context = test_view.get_context_data(test_name='test_value')
  352
+        self.assertEqual(context['test_name'], 'test_value')
3  tests/regressiontests/generic_views/tests.py
... ...
@@ -1,6 +1,7 @@
1 1
 from __future__ import absolute_import
2 2
 
3  
-from .base import ViewTest, TemplateViewTest, RedirectViewTest
  3
+from .base import (ViewTest, TemplateViewTest, RedirectViewTest,
  4
+    GetContextDataTest)
4 5
 from .dates import (ArchiveIndexViewTests, YearArchiveViewTests,
5 6
     MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests,
6 7
     DateDetailViewTests)
22  tests/regressiontests/generic_views/views.py
@@ -14,10 +14,9 @@ class CustomTemplateView(generic.TemplateView):
14 14
     template_name = 'generic_views/about.html'
15 15
 
16 16
     def get_context_data(self, **kwargs):
17  
-        return {
18  
-            'params': kwargs,
19  
-            'key': 'value'
20  
-        }
  17
+        context = super(CustomTemplateView, self).get_context_data(**kwargs)
  18
+        context.update({'key': 'value'})
  19
+        return context
21 20
 
22 21
 
23 22
 class ObjectDetail(generic.DetailView):
@@ -184,3 +183,18 @@ class BookDetailGetObjectCustomQueryset(BookDetail):
184 183
     def get_object(self, queryset=None):
185 184
         return super(BookDetailGetObjectCustomQueryset,self).get_object(
186 185
             queryset=Book.objects.filter(pk=2))
  186
+
  187
+class CustomContextView(generic.detail.SingleObjectMixin, generic.View):
  188
+    model = Book
  189
+    object = Book(name='dummy')
  190
+
  191
+    def get_object(self):
  192
+        return Book(name="dummy")
  193
+
  194
+    def get_context_data(self, **kwargs):
  195
+        context = {'custom_key': 'custom_value'}
  196
+        context.update(kwargs)
  197
+        return super(CustomContextView, self).get_context_data(**context)
  198
+
  199
+    def get_context_object_name(self, obj):
  200
+        return "test_name"

0 notes on commit 8663bc1

Please sign in to comment.
Something went wrong with that request. Please try again.