From 58683e9c82d6e7c5fbb7acef79eef9408b776ab0 Mon Sep 17 00:00:00 2001 From: Marc Tamlyn Date: Sat, 18 Aug 2012 14:46:17 +0100 Subject: [PATCH] Fixed #16744 -- Class based view should have the view object in the context Updated the most recent patch from @claudep to apply again and updated the documentation location. --- django/views/generic/base.py | 2 ++ docs/ref/class-based-views/mixins-simple.txt | 16 ++++++++++++++-- docs/ref/templates/api.txt | 2 ++ docs/releases/1.5.txt | 6 ++++++ tests/regressiontests/generic_views/base.py | 2 ++ tests/regressiontests/generic_views/detail.py | 2 ++ tests/regressiontests/generic_views/edit.py | 5 +++++ tests/regressiontests/generic_views/list.py | 2 ++ 8 files changed, 35 insertions(+), 2 deletions(-) diff --git a/django/views/generic/base.py b/django/views/generic/base.py index 6554e898ca..ca039ae9db 100644 --- a/django/views/generic/base.py +++ b/django/views/generic/base.py @@ -18,6 +18,8 @@ class ContextMixin(object): """ def get_context_data(self, **kwargs): + if 'view' not in kwargs: + kwargs['view'] = self return kwargs diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt index 085c8a5fc7..61fc945cd3 100644 --- a/docs/ref/class-based-views/mixins-simple.txt +++ b/docs/ref/class-based-views/mixins-simple.txt @@ -17,8 +17,20 @@ ContextMixin .. method:: get_context_data(**kwargs) - Returns a dictionary representing the template context. The - keyword arguments provided will make up the returned context. + Returns a dictionary representing the template context. The keyword + arguments provided will make up the returned context. + + The template context of all class-based generic views include a + ``view`` variable that points to the ``View`` instance. + + .. admonition:: Use ``alters_data`` where appropriate + + Note that having the view instance in the template context may + expose potentially hazardous methods to template authors. To + prevent methods like this from being called in the template, set + ``alters_data=True`` on those methods. For more information, read + the documentation on :ref:`rendering a template context + `. TemplateResponseMixin --------------------- diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index bd2b4c6e9d..c52194e6b7 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -198,6 +198,8 @@ straight lookups. Here are some things to keep in mind: * A variable can only be called if it has no required arguments. Otherwise, the system will return an empty string. +.. _alters-data-description: + * Obviously, there can be side effects when calling some variables, and it'd be either foolish or a security hole to allow the template system to access them. diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 2896acb36b..5f27b7ccbb 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -84,6 +84,12 @@ By passing ``False`` using this argument it is now possible to retreive the :class:`ContentType ` associated with proxy models. +New ``view`` variable in class-based views context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In all :doc:`generic class-based views ` +(or any class-based view inheriting from ``ContextMixin``), the context dictionary +contains a ``view`` variable that points to the ``View`` instance. + Minor features ~~~~~~~~~~~~~~ diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py index 5296f6941a..b9f64888fc 100644 --- a/tests/regressiontests/generic_views/base.py +++ b/tests/regressiontests/generic_views/base.py @@ -276,6 +276,7 @@ class TemplateViewTest(TestCase): response = self.client.get('/template/simple/bar/') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['params'], {'foo': 'bar'}) + self.assertTrue(isinstance(response.context['view'], View)) def test_extra_template_params(self): """ @@ -285,6 +286,7 @@ class TemplateViewTest(TestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.context['params'], {'foo': 'bar'}) self.assertEqual(response.context['key'], 'value') + self.assertTrue(isinstance(response.context['view'], View)) def test_cached_views(self): """ diff --git a/tests/regressiontests/generic_views/detail.py b/tests/regressiontests/generic_views/detail.py index 60c5f7afe9..c8f1999a2a 100644 --- a/tests/regressiontests/generic_views/detail.py +++ b/tests/regressiontests/generic_views/detail.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from django.core.exceptions import ImproperlyConfigured from django.test import TestCase +from django.views.generic.base import View from .models import Artist, Author, Page @@ -14,6 +15,7 @@ class DetailViewTest(TestCase): res = self.client.get('/detail/obj/') self.assertEqual(res.status_code, 200) self.assertEqual(res.context['object'], {'foo': 'bar'}) + self.assertTrue(isinstance(res.context['view'], View)) self.assertTemplateUsed(res, 'generic_views/detail.html') def test_detail_by_pk(self): diff --git a/tests/regressiontests/generic_views/edit.py b/tests/regressiontests/generic_views/edit.py index 651e14fff3..16f4da8efe 100644 --- a/tests/regressiontests/generic_views/edit.py +++ b/tests/regressiontests/generic_views/edit.py @@ -5,6 +5,7 @@ from django.core.urlresolvers import reverse from django import forms from django.test import TestCase from django.utils.unittest import expectedFailure +from django.views.generic.base import View from django.views.generic.edit import FormMixin from . import views @@ -31,6 +32,7 @@ class CreateViewTests(TestCase): res = self.client.get('/edit/authors/create/') self.assertEqual(res.status_code, 200) self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) self.assertFalse('object' in res.context) self.assertFalse('author' in res.context) self.assertTemplateUsed(res, 'generic_views/author_form.html') @@ -101,6 +103,7 @@ class CreateViewTests(TestCase): self.assertEqual(res.status_code, 302) self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') + class UpdateViewTests(TestCase): urls = 'regressiontests.generic_views.urls' @@ -226,6 +229,7 @@ class UpdateViewTests(TestCase): res = self.client.get('/edit/author/update/') self.assertEqual(res.status_code, 200) self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) self.assertTemplateUsed(res, 'generic_views/author_form.html') @@ -237,6 +241,7 @@ class UpdateViewTests(TestCase): self.assertRedirects(res, 'http://testserver/list/authors/') self.assertQuerysetEqual(Author.objects.all(), ['']) + class DeleteViewTests(TestCase): urls = 'regressiontests.generic_views.urls' diff --git a/tests/regressiontests/generic_views/list.py b/tests/regressiontests/generic_views/list.py index b925758524..14dc1d725d 100644 --- a/tests/regressiontests/generic_views/list.py +++ b/tests/regressiontests/generic_views/list.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from django.core.exceptions import ImproperlyConfigured from django.test import TestCase +from django.views.generic.base import View from .models import Author, Artist @@ -21,6 +22,7 @@ class ListViewTests(TestCase): self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'generic_views/author_list.html') self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertTrue(isinstance(res.context['view'], View)) self.assertIs(res.context['author_list'], res.context['object_list']) self.assertIsNone(res.context['paginator']) self.assertIsNone(res.context['page_obj'])