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:
parent
b4a9827133
commit
8663bc1103
|
@ -8,6 +8,16 @@ from django.utils.decorators import classonlymethod
|
||||||
logger = getLogger('django.request')
|
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):
|
class View(object):
|
||||||
"""
|
"""
|
||||||
Intentionally simple parent class for all views. Only implements
|
Intentionally simple parent class for all views. Only implements
|
||||||
|
@ -110,17 +120,13 @@ class TemplateResponseMixin(object):
|
||||||
return [self.template_name]
|
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):
|
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)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -217,16 +217,6 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
||||||
|
|
||||||
return date_list
|
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):
|
class BaseArchiveIndexView(BaseDateListView):
|
||||||
"""
|
"""
|
||||||
Base class for archives of date-based items.
|
Base class for archives of date-based items.
|
||||||
|
|
|
@ -2,10 +2,10 @@ from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.utils.translation import ugettext as _
|
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.
|
Provides the ability to retrieve a single object for further manipulation.
|
||||||
"""
|
"""
|
||||||
|
@ -86,11 +86,12 @@ class SingleObjectMixin(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = kwargs
|
context = {}
|
||||||
context_object_name = self.get_context_object_name(self.object)
|
context_object_name = self.get_context_object_name(self.object)
|
||||||
if context_object_name:
|
if context_object_name:
|
||||||
context[context_object_name] = self.object
|
context[context_object_name] = self.object
|
||||||
return context
|
context.update(kwargs)
|
||||||
|
return super(SingleObjectMixin, self).get_context_data(**context)
|
||||||
|
|
||||||
|
|
||||||
class BaseDetailView(SingleObjectMixin, View):
|
class BaseDetailView(SingleObjectMixin, View):
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from django.forms import models as model_forms
|
from django.forms import models as model_forms
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.http import HttpResponseRedirect
|
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,
|
from django.views.generic.detail import (SingleObjectMixin,
|
||||||
SingleObjectTemplateResponseMixin, BaseDetailView)
|
SingleObjectTemplateResponseMixin, BaseDetailView)
|
||||||
|
|
||||||
|
|
||||||
class FormMixin(object):
|
class FormMixin(ContextMixin):
|
||||||
"""
|
"""
|
||||||
A mixin that provides a way to show and handle a form in a request.
|
A mixin that provides a way to show and handle a form in a request.
|
||||||
"""
|
"""
|
||||||
|
@ -45,9 +45,6 @@ class FormMixin(object):
|
||||||
})
|
})
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
if self.success_url:
|
if self.success_url:
|
||||||
url = self.success_url
|
url = self.success_url
|
||||||
|
@ -113,13 +110,14 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
|
||||||
return super(ModelFormMixin, self).form_valid(form)
|
return super(ModelFormMixin, self).form_valid(form)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = kwargs
|
context = {}
|
||||||
if self.object:
|
if self.object:
|
||||||
context['object'] = self.object
|
context['object'] = self.object
|
||||||
context_object_name = self.get_context_object_name(self.object)
|
context_object_name = self.get_context_object_name(self.object)
|
||||||
if context_object_name:
|
if context_object_name:
|
||||||
context[context_object_name] = self.object
|
context[context_object_name] = self.object
|
||||||
return context
|
context.update(kwargs)
|
||||||
|
return super(ModelFormMixin, self).get_context_data(**context)
|
||||||
|
|
||||||
|
|
||||||
class ProcessFormView(View):
|
class ProcessFormView(View):
|
||||||
|
|
|
@ -3,10 +3,10 @@ from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.utils.translation import ugettext as _
|
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
|
allow_empty = True
|
||||||
queryset = None
|
queryset = None
|
||||||
model = None
|
model = None
|
||||||
|
@ -103,10 +103,10 @@ class MultipleObjectMixin(object):
|
||||||
'is_paginated': False,
|
'is_paginated': False,
|
||||||
'object_list': queryset
|
'object_list': queryset
|
||||||
}
|
}
|
||||||
context.update(kwargs)
|
|
||||||
if context_object_name is not None:
|
if context_object_name is not None:
|
||||||
context[context_object_name] = queryset
|
context[context_object_name] = queryset
|
||||||
return context
|
context.update(kwargs)
|
||||||
|
return super(MultipleObjectMixin, self).get_context_data(**context)
|
||||||
|
|
||||||
|
|
||||||
class BaseListView(MultipleObjectMixin, View):
|
class BaseListView(MultipleObjectMixin, View):
|
||||||
|
|
|
@ -270,6 +270,16 @@ more::
|
||||||
context['book_list'] = Book.objects.all()
|
context['book_list'] = Book.objects.all()
|
||||||
return context
|
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
|
Viewing subsets of objects
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
@ -6,6 +8,7 @@ from django.test import TestCase, RequestFactory
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
from django.views.generic import View, TemplateView, RedirectView
|
from django.views.generic import View, TemplateView, RedirectView
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
class SimpleView(View):
|
class SimpleView(View):
|
||||||
"""
|
"""
|
||||||
|
@ -331,3 +334,19 @@ class RedirectViewTest(unittest.TestCase):
|
||||||
# we can't use self.rf.get because it always sets QUERY_STRING
|
# 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/'))
|
response = RedirectView.as_view(url='/bar/')(self.rf.request(PATH_INFO='/foo/'))
|
||||||
self.assertEqual(response.status_code, 301)
|
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')
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from .base import ViewTest, TemplateViewTest, RedirectViewTest
|
from .base import (ViewTest, TemplateViewTest, RedirectViewTest,
|
||||||
|
GetContextDataTest)
|
||||||
from .dates import (ArchiveIndexViewTests, YearArchiveViewTests,
|
from .dates import (ArchiveIndexViewTests, YearArchiveViewTests,
|
||||||
MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests,
|
MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests,
|
||||||
DateDetailViewTests)
|
DateDetailViewTests)
|
||||||
|
|
|
@ -14,10 +14,9 @@ class CustomTemplateView(generic.TemplateView):
|
||||||
template_name = 'generic_views/about.html'
|
template_name = 'generic_views/about.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
return {
|
context = super(CustomTemplateView, self).get_context_data(**kwargs)
|
||||||
'params': kwargs,
|
context.update({'key': 'value'})
|
||||||
'key': 'value'
|
return context
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectDetail(generic.DetailView):
|
class ObjectDetail(generic.DetailView):
|
||||||
|
@ -184,3 +183,18 @@ class BookDetailGetObjectCustomQueryset(BookDetail):
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
return super(BookDetailGetObjectCustomQueryset,self).get_object(
|
return super(BookDetailGetObjectCustomQueryset,self).get_object(
|
||||||
queryset=Book.objects.filter(pk=2))
|
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"
|
||||||
|
|
Loading…
Reference in New Issue