From b3d2091681d31bf2a6f8c4a1f9f1fb32393864c8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 21 Dec 2010 17:18:41 +0000 Subject: [PATCH] Fixed #12816 -- Added a render() shortcut. git-svn-id: http://code.djangoproject.com/svn/django/trunk@15008 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/shortcuts/__init__.py | 32 +++++++--- docs/releases/1.3.txt | 5 ++ docs/topics/http/shortcuts.txt | 64 +++++++++++++++++++ .../views/templates/debug/render_test.html | 2 +- tests/regressiontests/views/tests/__init__.py | 1 + .../regressiontests/views/tests/shortcuts.py | 53 +++++++++++++++ tests/regressiontests/views/urls.py | 8 +++ tests/regressiontests/views/views.py | 44 ++++++++++++- 8 files changed, 195 insertions(+), 14 deletions(-) create mode 100644 tests/regressiontests/views/tests/shortcuts.py diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py index 119bd092b5..00e97b1107 100644 --- a/django/shortcuts/__init__.py +++ b/django/shortcuts/__init__.py @@ -4,7 +4,7 @@ of MVC. In other words, these functions/classes introduce controlled coupling for convenience's sake. """ -from django.template import loader +from django.template import loader, RequestContext from django.http import HttpResponse, Http404 from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.db.models.manager import Manager @@ -19,20 +19,31 @@ def render_to_response(*args, **kwargs): httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)} return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs) +def render(request, *args, **kwargs): + """ + Returns a HttpResponse whose content is filled with the result of calling + django.template.loader.render_to_string() with the passed arguments. + Uses a RequestContext by default. + """ + httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)} + kwargs['context_instance'] = kwargs.get('context_instance', RequestContext(request)) + return HttpResponse(loader.render_to_string(*args, **kwargs), + **httpresponse_kwargs) + def redirect(to, *args, **kwargs): """ Returns an HttpResponseRedirect to the apropriate URL for the arguments passed. - + The arguments could be: - + * A model: the model's `get_absolute_url()` function will be called. - + * A view name, possibly with arguments: `urlresolvers.reverse()` will be used to reverse-resolve the name. - + * A URL, which will be used as-is for the redirect location. - + By default issues a temporary redirect; pass permanent=True to issue a permanent redirect """ @@ -40,11 +51,11 @@ def redirect(to, *args, **kwargs): redirect_class = HttpResponsePermanentRedirect else: redirect_class = HttpResponseRedirect - + # If it's a model, use get_absolute_url() if hasattr(to, 'get_absolute_url'): return redirect_class(to.get_absolute_url()) - + # Next try a reverse URL resolution. try: return redirect_class(urlresolvers.reverse(to, args=args, kwargs=kwargs)) @@ -55,7 +66,7 @@ def redirect(to, *args, **kwargs): # If this doesn't "feel" like a URL, re-raise. if '/' not in to and '.' not in to: raise - + # Finally, fall back and assume it's a URL return redirect_class(to) @@ -101,4 +112,5 @@ def get_list_or_404(klass, *args, **kwargs): obj_list = list(queryset.filter(*args, **kwargs)) if not obj_list: raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) - return obj_list \ No newline at end of file + return obj_list + diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt index 068670f9f2..d111eaeffb 100644 --- a/docs/releases/1.3.txt +++ b/docs/releases/1.3.txt @@ -215,6 +215,11 @@ requests. These include: making it easier to write simple template tags that require access to template context. + * A new :meth:`~django.shortcuts.render()` shortcut -- an + alternative to :meth:`~django.shortcuts.render_to_response()` + providing a :class:`~django.template.RequestContext` by + default. + .. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly .. _backwards-incompatible-changes-1.3: diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt index 315460e6c3..d99429dbce 100644 --- a/docs/topics/http/shortcuts.txt +++ b/docs/topics/http/shortcuts.txt @@ -12,6 +12,70 @@ The package ``django.shortcuts`` collects helper functions and classes that "span" multiple levels of MVC. In other words, these functions/classes introduce controlled coupling for convenience's sake. +``render`` +========== + +.. function:: render(request, template[, dictionary][, context_instance][, mimetype]) + + Combines a given template with a given context dictionary and returns an + :class:`~django.http.HttpResponse` object with that rendered text. + + :func:`render()` is the same as a call to + :func:`render_to_response()` with a context_instance argument that + that forces the use of a :class:`RequestContext`. + +Required arguments +------------------ + +``request`` + The request object used to generate this response. + +``template`` + The full name of a template to use or sequence of template names. + +Optional arguments +------------------ + +``dictionary`` + A dictionary of values to add to the template context. By default, this + is an empty dictionary. If a value in the dictionary is callable, the + view will call it just before rendering the template. + +``context_instance`` + The context instance to render the template with. By default, the template + will be rendered with a ``RequestContext`` instance (filled with values from + ``request`` and ```dictionary``). + +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the :setting:`DEFAULT_CONTENT_TYPE` setting. + +Example +------- + +The following example renders the template ``myapp/index.html`` with the +MIME type ``application/xhtml+xml``:: + + from django.shortcuts import render_to_response + + def my_view(request): + # View code here... + return render_to_response('myapp/index.html', {"foo": "bar"}, + mimetype="application/xhtml+xml") + +This example is equivalent to:: + + from django.http import HttpResponse + from django.template import Context, loader + + def my_view(request): + # View code here... + t = loader.get_template('myapp/template.html') + c = RequestContext(request, {'foo': 'bar'}) + return HttpResponse(t.render(c), + mimetype="application/xhtml+xml") + + ``render_to_response`` ====================== diff --git a/tests/regressiontests/views/templates/debug/render_test.html b/tests/regressiontests/views/templates/debug/render_test.html index 259cb80a5c..1b2f47a0e3 100644 --- a/tests/regressiontests/views/templates/debug/render_test.html +++ b/tests/regressiontests/views/templates/debug/render_test.html @@ -1 +1 @@ -{{ foo }}.{{ bar }}.{{ baz }}.{{ processors }} +{{ foo }}.{{ bar }}.{{ baz }}.{{ STATIC_URL }} diff --git a/tests/regressiontests/views/tests/__init__.py b/tests/regressiontests/views/tests/__init__.py index bc28f3e9d0..b888dde5dc 100644 --- a/tests/regressiontests/views/tests/__init__.py +++ b/tests/regressiontests/views/tests/__init__.py @@ -5,5 +5,6 @@ from generic.date_based import * from generic.object_list import * from generic.simple import * from i18n import * +from shortcuts import * from specials import * from static import * diff --git a/tests/regressiontests/views/tests/shortcuts.py b/tests/regressiontests/views/tests/shortcuts.py new file mode 100644 index 0000000000..fe1ff5b49e --- /dev/null +++ b/tests/regressiontests/views/tests/shortcuts.py @@ -0,0 +1,53 @@ +from django.conf import settings +from django.test import TestCase + +class ShortcutTests(TestCase): + def setUp(self): + self.old_STATIC_URL = settings.STATIC_URL + self.old_TEMPLATE_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS + + settings.STATIC_URL = '/path/to/static/media' + settings.TEMPLATE_CONTEXT_PROCESSORS = ( + 'django.core.context_processors.static' + ) + + def tearDown(self): + settings.STATIC_URL = self.old_STATIC_URL + settings.TEMPLATE_CONTEXT_PROCESSORS = self.old_TEMPLATE_CONTEXT_PROCESSORS + + def test_render_to_response(self): + response = self.client.get('/views/shortcuts/render_to_response/') + self.assertEquals(response.status_code, 200) + self.assertEquals(response.content, 'FOO.BAR..\n') + self.assertEquals(response['Content-Type'], 'text/html; charset=utf-8') + + def test_render_to_response_with_request_context(self): + response = self.client.get('/views/shortcuts/render_to_response/request_context/') + self.assertEquals(response.status_code, 200) + self.assertEquals(response.content, 'FOO.BAR../path/to/static/media\n') + self.assertEquals(response['Content-Type'], 'text/html; charset=utf-8') + + def test_render_to_response_with_mimetype(self): + response = self.client.get('/views/shortcuts/render_to_response/mimetype/') + self.assertEquals(response.status_code, 200) + self.assertEquals(response.content, 'FOO.BAR..\n') + self.assertEquals(response['Content-Type'], 'application/x-rendertest') + + def test_render(self): + response = self.client.get('/views/shortcuts/render/') + self.assertEquals(response.status_code, 200) + self.assertEquals(response.content, 'FOO.BAR../path/to/static/media\n') + self.assertEquals(response['Content-Type'], 'text/html; charset=utf-8') + + def test_render_with_base_context(self): + response = self.client.get('/views/shortcuts/render/base_context/') + self.assertEquals(response.status_code, 200) + self.assertEquals(response.content, 'FOO.BAR..\n') + self.assertEquals(response['Content-Type'], 'text/html; charset=utf-8') + + def test_render_with_mimetype(self): + response = self.client.get('/views/shortcuts/render/mimetype/') + self.assertEquals(response.status_code, 200) + self.assertEquals(response.content, 'FOO.BAR../path/to/static/media\n') + self.assertEquals(response['Content-Type'], 'application/x-rendertest') + diff --git a/tests/regressiontests/views/urls.py b/tests/regressiontests/views/urls.py index 0b46d115bf..b1ca432e9f 100644 --- a/tests/regressiontests/views/urls.py +++ b/tests/regressiontests/views/urls.py @@ -143,6 +143,14 @@ urlpatterns += patterns('django.views.generic.simple', urlpatterns += patterns('regressiontests.views.views', url(r'view_exception/(?P\d+)/$', 'view_exception', name='view_exception'), url(r'template_exception/(?P\d+)/$', 'template_exception', name='template_exception'), + + (r'^shortcuts/render_to_response/$', 'render_to_response_view'), + (r'^shortcuts/render_to_response/request_context/$', 'render_to_response_view_with_request_context'), + (r'^shortcuts/render_to_response/mimetype/$', 'render_to_response_view_with_mimetype'), + (r'^shortcuts/render/$', 'render_view'), + (r'^shortcuts/render/base_context/$', 'render_view_with_base_context'), + (r'^shortcuts/render/mimetype/$', 'render_view_with_mimetype'), + ) # simple generic views. diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py index 445b4ed312..0705bd0188 100644 --- a/tests/regressiontests/views/views.py +++ b/tests/regressiontests/views/views.py @@ -1,11 +1,12 @@ import sys -from django.http import HttpResponse, HttpResponseRedirect from django import forms +from django.http import HttpResponse, HttpResponseRedirect +from django.core.urlresolvers import get_resolver +from django.shortcuts import render_to_response, render +from django.template import Context, RequestContext from django.views.debug import technical_500_response from django.views.generic.create_update import create_object -from django.core.urlresolvers import get_resolver -from django.shortcuts import render_to_response from regressiontests.views import BrokenException, except_args @@ -57,3 +58,40 @@ def template_exception(request, n): return render_to_response('debug/template_exception.html', {'arg': except_args[int(n)]}) +# Some views to exercise the shortcuts + +def render_to_response_view(request): + return render_to_response('debug/render_test.html', { + 'foo': 'FOO', + 'bar': 'BAR', + }) + +def render_to_response_view_with_request_context(request): + return render_to_response('debug/render_test.html', { + 'foo': 'FOO', + 'bar': 'BAR', + }, context_instance=RequestContext(request)) + +def render_to_response_view_with_mimetype(request): + return render_to_response('debug/render_test.html', { + 'foo': 'FOO', + 'bar': 'BAR', + }, mimetype='application/x-rendertest') + +def render_view(request): + return render(request, 'debug/render_test.html', { + 'foo': 'FOO', + 'bar': 'BAR', + }) + +def render_view_with_base_context(request): + return render(request, 'debug/render_test.html', { + 'foo': 'FOO', + 'bar': 'BAR', + }, context_instance=Context()) + +def render_view_with_mimetype(request): + return render(request, 'debug/render_test.html', { + 'foo': 'FOO', + 'bar': 'BAR', + }, mimetype='application/x-rendertest')