Code

Ticket #16074: 16074-1.diff

File 16074-1.diff, 10.5 KB (added by claudep, 2 years ago)

Pull request + minor changes

Line 
1diff --git a/django/views/generic/base.py b/django/views/generic/base.py
2index fcdc7c7..431cb1e 100644
3--- a/django/views/generic/base.py
4+++ b/django/views/generic/base.py
5@@ -8,6 +8,16 @@ from django.utils.decorators import classonlymethod
6 logger = getLogger('django.request')
7 
8 
9+class ContextMixin(object):
10+    """
11+    A default context mixin that passes the keyword arguments received by
12+    get_context_data as the template context.
13+    """
14+
15+    def get_context_data(self, **kwargs):
16+        return kwargs
17+
18+
19 class View(object):
20     """
21     Intentionally simple parent class for all views. Only implements
22@@ -110,17 +120,17 @@ class TemplateResponseMixin(object):
23             return [self.template_name]
24 
25 
26-class TemplateView(TemplateResponseMixin, View):
27+class TemplateView(TemplateResponseMixin, ContextMixin, View):
28     """
29-    A view that renders a template.
30+    A view that renders a template.  This view is different from all the others
31+    insofar as it also passes ``kwargs`` as ``params`` to the template context
32+    (this functionality has been deprecated).
33     """
34     def get_context_data(self, **kwargs):
35-        return {
36-            'params': kwargs
37-        }
38+        return super(TemplateView, self).get_context_data(**kwargs)
39 
40     def get(self, request, *args, **kwargs):
41-        context = self.get_context_data(**kwargs)
42+        context = self.get_context_data(params=kwargs)
43         return self.render_to_response(context)
44 
45 
46diff --git a/django/views/generic/dates.py b/django/views/generic/dates.py
47index 5f5f959..e5c3e5b 100644
48--- a/django/views/generic/dates.py
49+++ b/django/views/generic/dates.py
50@@ -217,16 +217,6 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
51 
52         return date_list
53 
54-    def get_context_data(self, **kwargs):
55-        """
56-        Get the context. Must return a Context (or subclass) instance.
57-        """
58-        items = kwargs.pop('object_list')
59-        context = super(BaseDateListView, self).get_context_data(object_list=items)
60-        context.update(kwargs)
61-        return context
62-
63-
64 class BaseArchiveIndexView(BaseDateListView):
65     """
66     Base class for archives of date-based items.
67diff --git a/django/views/generic/detail.py b/django/views/generic/detail.py
68index b9278bb..a2adb15 100644
69--- a/django/views/generic/detail.py
70+++ b/django/views/generic/detail.py
71@@ -2,10 +2,10 @@ from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
72 from django.http import Http404
73 from django.utils.encoding import smart_str
74 from django.utils.translation import ugettext as _
75-from django.views.generic.base import TemplateResponseMixin, View
76+from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
77 
78 
79-class SingleObjectMixin(object):
80+class SingleObjectMixin(ContextMixin):
81     """
82     Provides the ability to retrieve a single object for further manipulation.
83     """
84@@ -86,11 +86,12 @@ class SingleObjectMixin(object):
85             return None
86 
87     def get_context_data(self, **kwargs):
88-        context = kwargs
89+        context = {}
90         context_object_name = self.get_context_object_name(self.object)
91         if context_object_name:
92             context[context_object_name] = self.object
93-        return context
94+        context.update(kwargs)
95+        return super(SingleObjectMixin, self).get_context_data(**context)
96 
97 
98 class BaseDetailView(SingleObjectMixin, View):
99diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py
100index d107e9a..1f488cb 100644
101--- a/django/views/generic/edit.py
102+++ b/django/views/generic/edit.py
103@@ -1,12 +1,12 @@
104 from django.forms import models as model_forms
105 from django.core.exceptions import ImproperlyConfigured
106 from django.http import HttpResponseRedirect
107-from django.views.generic.base import TemplateResponseMixin, View
108+from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
109 from django.views.generic.detail import (SingleObjectMixin,
110                         SingleObjectTemplateResponseMixin, BaseDetailView)
111 
112 
113-class FormMixin(object):
114+class FormMixin(ContextMixin):
115     """
116     A mixin that provides a way to show and handle a form in a request.
117     """
118@@ -45,9 +45,6 @@ class FormMixin(object):
119             })
120         return kwargs
121 
122-    def get_context_data(self, **kwargs):
123-        return kwargs
124-
125     def get_success_url(self):
126         if self.success_url:
127             url = self.success_url
128@@ -113,13 +110,14 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
129         return super(ModelFormMixin, self).form_valid(form)
130 
131     def get_context_data(self, **kwargs):
132-        context = kwargs
133+        context = {}
134         if self.object:
135             context['object'] = self.object
136             context_object_name = self.get_context_object_name(self.object)
137             if context_object_name:
138                 context[context_object_name] = self.object
139-        return context
140+        context.update(kwargs)
141+        return super(ModelFormMixin, self).get_context_data(**context)
142 
143 
144 class ProcessFormView(View):
145diff --git a/django/views/generic/list.py b/django/views/generic/list.py
146index 9797356..d4664c3 100644
147--- a/django/views/generic/list.py
148+++ b/django/views/generic/list.py
149@@ -3,10 +3,10 @@ from django.core.exceptions import ImproperlyConfigured
150 from django.http import Http404
151 from django.utils.encoding import smart_str
152 from django.utils.translation import ugettext as _
153-from django.views.generic.base import TemplateResponseMixin, View
154+from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
155 
156 
157-class MultipleObjectMixin(object):
158+class MultipleObjectMixin(ContextMixin):
159     allow_empty = True
160     queryset = None
161     model = None
162@@ -103,10 +103,10 @@ class MultipleObjectMixin(object):
163                 'is_paginated': False,
164                 'object_list': queryset
165             }
166-        context.update(kwargs)
167         if context_object_name is not None:
168             context[context_object_name] = queryset
169-        return context
170+        context.update(kwargs)
171+        return super(MultipleObjectMixin, self).get_context_data(**context)
172 
173 
174 class BaseListView(MultipleObjectMixin, View):
175diff --git a/docs/topics/class-based-views.txt b/docs/topics/class-based-views.txt
176index 8059ed1..e1e2618 100644
177--- a/docs/topics/class-based-views.txt
178+++ b/docs/topics/class-based-views.txt
179@@ -270,6 +270,16 @@ more::
180             context['book_list'] = Book.objects.all()
181             return context
182 
183+.. note::
184+
185+    Generally, get_context_data will merge the context data of all parent classes
186+    with those of the current class.  To preserve this behavior in your own classes
187+    where you want to alter the context, you should be sure to call
188+    get_context_data on the super class. When no two classes try to define the same
189+    key, this will give the expected results. However if any class attempts to
190+    override a key after parent classes have set it (after the call to super), any
191+    children of that class will also need to explictly set it after super if they
192+    want to be sure to override all parents.
193 
194 Viewing subsets of objects
195 --------------------------
196diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py
197index 6528dc6..e18ed2a 100644
198--- a/tests/regressiontests/generic_views/base.py
199+++ b/tests/regressiontests/generic_views/base.py
200@@ -1,3 +1,5 @@
201+from __future__ import absolute_import
202+
203 import time
204 
205 from django.core.exceptions import ImproperlyConfigured
206@@ -6,6 +8,7 @@ from django.test import TestCase, RequestFactory
207 from django.utils import unittest
208 from django.views.generic import View, TemplateView, RedirectView
209 
210+from . import views
211 
212 class SimpleView(View):
213     """
214@@ -331,3 +334,19 @@ class RedirectViewTest(unittest.TestCase):
215         # we can't use self.rf.get because it always sets QUERY_STRING
216         response = RedirectView.as_view(url='/bar/')(self.rf.request(PATH_INFO='/foo/'))
217         self.assertEqual(response.status_code, 301)
218+
219+
220+class GetContextDataTest(unittest.TestCase):
221+
222+    def test_get_context_data_super(self):
223+        test_view = views.CustomContextView()
224+        context = test_view.get_context_data(kwarg_test='kwarg_value')
225+
226+        # the test_name key is inserted by the test classes parent
227+        self.assertTrue('test_name' in context)
228+        self.assertEqual(context['kwarg_test'], 'kwarg_value')
229+        self.assertEqual(context['custom_key'], 'custom_value')
230+
231+        # test that kwarg overrides values assigned higher up
232+        context = test_view.get_context_data(test_name='test_value')
233+        self.assertEqual(context['test_name'], 'test_value')
234diff --git a/tests/regressiontests/generic_views/tests.py b/tests/regressiontests/generic_views/tests.py
235index 72aab03..c985ad3 100644
236--- a/tests/regressiontests/generic_views/tests.py
237+++ b/tests/regressiontests/generic_views/tests.py
238@@ -1,6 +1,7 @@
239 from __future__ import absolute_import
240 
241-from .base import ViewTest, TemplateViewTest, RedirectViewTest
242+from .base import (ViewTest, TemplateViewTest, RedirectViewTest,
243+    GetContextDataTest)
244 from .dates import (ArchiveIndexViewTests, YearArchiveViewTests,
245     MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests,
246     DateDetailViewTests)
247diff --git a/tests/regressiontests/generic_views/views.py b/tests/regressiontests/generic_views/views.py
248index 5ff9cf0..1e70ba4 100644
249--- a/tests/regressiontests/generic_views/views.py
250+++ b/tests/regressiontests/generic_views/views.py
251@@ -14,10 +14,9 @@ class CustomTemplateView(generic.TemplateView):
252     template_name = 'generic_views/about.html'
253 
254     def get_context_data(self, **kwargs):
255-        return {
256-            'params': kwargs,
257-            'key': 'value'
258-        }
259+        context = super(CustomTemplateView, self).get_context_data(**kwargs)
260+        context.update({'key': 'value'})
261+        return context
262 
263 
264 class ObjectDetail(generic.DetailView):
265@@ -184,3 +183,18 @@ class BookDetailGetObjectCustomQueryset(BookDetail):
266     def get_object(self, queryset=None):
267         return super(BookDetailGetObjectCustomQueryset,self).get_object(
268             queryset=Book.objects.filter(pk=2))
269+
270+class CustomContextView(generic.detail.SingleObjectMixin, generic.View):
271+    model = Book
272+    object = Book(name='dummy')
273+
274+    def get_object(self):
275+        return Book(name="dummy")
276+
277+    def get_context_data(self, **kwargs):
278+        context = {'custom_key': 'custom_value'}
279+        context.update(kwargs)
280+        return super(CustomContextView, self).get_context_data(**context)
281+
282+    def get_context_object_name(self, obj):
283+        return "test_name"