Skip to content

Commit

Permalink
Fixed #12816 -- Added a render() shortcut.
Browse files Browse the repository at this point in the history
git-svn-id: http://code.djangoproject.com/svn/django/trunk@15008 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
russellm committed Dec 21, 2010
1 parent e77adc9 commit 98004b5
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 14 deletions.
32 changes: 22 additions & 10 deletions django/shortcuts/__init__.py
Expand Up @@ -4,7 +4,7 @@
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
Expand All @@ -19,32 +19,43 @@ 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
"""
if kwargs.pop('permanent', False):
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))
Expand All @@ -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)

Expand Down Expand Up @@ -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
return obj_list

5 changes: 5 additions & 0 deletions docs/releases/1.3.txt
Expand Up @@ -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:
Expand Down
64 changes: 64 additions & 0 deletions docs/topics/http/shortcuts.txt
Expand Up @@ -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``
======================

Expand Down
@@ -1 +1 @@
{{ foo }}.{{ bar }}.{{ baz }}.{{ processors }}
{{ foo }}.{{ bar }}.{{ baz }}.{{ STATIC_URL }}
1 change: 1 addition & 0 deletions tests/regressiontests/views/tests/__init__.py
Expand Up @@ -5,5 +5,6 @@
from generic.object_list import *
from generic.simple import *
from i18n import *
from shortcuts import *
from specials import *
from static import *
53 changes: 53 additions & 0 deletions 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')

8 changes: 8 additions & 0 deletions tests/regressiontests/views/urls.py
Expand Up @@ -143,6 +143,14 @@
urlpatterns += patterns('regressiontests.views.views',
url(r'view_exception/(?P<n>\d+)/$', 'view_exception', name='view_exception'),
url(r'template_exception/(?P<n>\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.
Expand Down
44 changes: 41 additions & 3 deletions 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

Expand Down Expand Up @@ -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')

0 comments on commit 98004b5

Please sign in to comment.