diff --git a/django/views/generic/base.py b/django/views/generic/base.py
index fcdc7c7..431cb1e 100644
a
|
b
|
from django.utils.decorators import classonlymethod
|
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 |
… |
… |
class TemplateResponseMixin(object):
|
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 |
| 127 | (this functionality has been deprecated). |
116 | 128 | """ |
117 | 129 | def get_context_data(self, **kwargs): |
118 | | return { |
119 | | 'params': kwargs |
120 | | } |
| 130 | return super(TemplateView, self).get_context_data(**kwargs) |
121 | 131 | |
122 | 132 | def get(self, request, *args, **kwargs): |
123 | | context = self.get_context_data(**kwargs) |
| 133 | context = self.get_context_data(params=kwargs) |
124 | 134 | return self.render_to_response(context) |
125 | 135 | |
126 | 136 | |
diff --git a/django/views/generic/dates.py b/django/views/generic/dates.py
index 5f5f959..e5c3e5b 100644
a
|
b
|
class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
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. |
diff --git a/django/views/generic/detail.py b/django/views/generic/detail.py
index b9278bb..a2adb15 100644
a
|
b
|
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
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 | """ |
… |
… |
class SingleObjectMixin(object):
|
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): |
diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py
index d107e9a..1f488cb 100644
a
|
b
|
|
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 | """ |
… |
… |
class FormMixin(object):
|
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 |
… |
… |
class ModelFormMixin(FormMixin, SingleObjectMixin):
|
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): |
diff --git a/django/views/generic/list.py b/django/views/generic/list.py
index 9797356..d4664c3 100644
a
|
b
|
from django.core.exceptions import ImproperlyConfigured
|
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 |
… |
… |
class MultipleObjectMixin(object):
|
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): |
diff --git a/docs/topics/class-based-views.txt b/docs/topics/class-based-views.txt
index 8059ed1..e1e2618 100644
a
|
b
|
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 | -------------------------- |
diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py
index 6528dc6..e18ed2a 100644
a
|
b
|
|
| 1 | from __future__ import absolute_import |
| 2 | |
1 | 3 | import time |
2 | 4 | |
3 | 5 | from django.core.exceptions import ImproperlyConfigured |
… |
… |
from django.test import TestCase, RequestFactory
|
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 | """ |
… |
… |
class RedirectViewTest(unittest.TestCase):
|
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') |
diff --git a/tests/regressiontests/generic_views/tests.py b/tests/regressiontests/generic_views/tests.py
index 72aab03..c985ad3 100644
a
|
b
|
|
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) |
diff --git a/tests/regressiontests/generic_views/views.py b/tests/regressiontests/generic_views/views.py
index 5ff9cf0..1e70ba4 100644
a
|
b
|
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): |
… |
… |
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" |