Fixed #10194: added `django.shortcuts.redirect`, a do-what-I-mean redirect shortcut. See the docs at topics/http/shortcuts for details.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10108 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2009-03-21 13:09:13 +00:00
parent 4eadcf45d4
commit d7e8127524
3 changed files with 160 additions and 15 deletions

View File

@ -6,8 +6,10 @@ for convenience's sake.
from django.template import loader
from django.http import HttpResponse, Http404
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.db.models.manager import Manager
from django.db.models.query import QuerySet
from django.core import urlresolvers
def render_to_response(*args, **kwargs):
"""
@ -17,6 +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 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))
except urlresolvers.NoReverseMatch:
# 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)
def _get_queryset(klass):
"""
Returns a QuerySet from a Model, Manager, or QuerySet. Created to make
@ -59,4 +98,4 @@ 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

View File

@ -4,16 +4,23 @@
Django shortcut functions
=========================
.. module:: django.shortcuts
:synopsis:
Convience shortcuts that spam multiple levels of Django's MVC stack.
.. index:: shortcuts
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_to_response()``
========================
``render_to_response``
======================
``django.shortcuts.render_to_response`` renders a given template with a given
context dictionary and returns an ``HttpResponse`` object with that rendered
text.
.. function:: render_to_response(template[, dictionary][, context_instance][, mimetype])
Renders a given template with a given context dictionary and returns an
:class:`~django.http.HttpResponse` object with that rendered text.
Required arguments
------------------
@ -42,9 +49,8 @@ Optional arguments
context_instance=RequestContext(request))
``mimetype``
.. versionadded:: 1.0
.. versionadded:: 1.0
The MIME type to use for the resulting document. Defaults to the value of
the :setting:`DEFAULT_CONTENT_TYPE` setting.
@ -73,12 +79,77 @@ This example is equivalent to::
r = HttpResponse(t.render(c),
mimetype="application/xhtml+xml")
``redirect``
============
.. function:: redirect(to[, permanent=False], *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
Examples
--------
You can use the :func:`redirect` function in a number of ways.
1. By passing some object; that object's
:meth:`~django.db.models.Model.get_absolute_url` method will be called
to figure out the redirect URL::
def my_view(request):
...
object = MyModel.objects.get(...)
return redirect(object)
2. By passing the name of a view and optionally some positional or
keyword arguments; the URL will be reverse resolved using the
:func:`~django.core.urlresolvers.reverse` method::
def my_view(request):
...
return redirect('some-view-name', foo='bar')
3. By passing a hardcoded URL to redirect to::
def my_view(request):
...
return redirect('/some/url/')
This also works with full URLs::
def my_view(request):
...
return redirect('http://example.com/')
By default, :func:`redirect` returns a temporary redirect. All of the above
forms accept a ``permanent`` argument; if set to ``True`` a permanent redirect
will be returned::
def my_view(request):
...
object = MyModel.objects.get(...)
return redirect(object, permanent=True)
``get_object_or_404``
=====================
``django.shortcuts.get_object_or_404`` calls
:meth:`~django.db.models.QuerySet.get()` on a given model manager, but it raises
``django.http.Http404`` instead of the model's ``DoesNotExist`` exception.
.. function:: get_object_or_404(object, *args, **kwargs)
Calls :meth:`~django.db.models.QuerySet.get()` on a given model manager,
but it raises ``django.http.Http404`` instead of the model's
``DoesNotExist`` exception.
Required arguments
------------------
@ -118,9 +189,11 @@ raised if more than one object is found.
``get_list_or_404``
===================
``django.shortcuts.get_list_or_404`` returns the result of
:meth:`~django.db.models.QuerySet.filter()` on a given model manager, raising
``django.http.Http404`` if the resulting list is empty.
.. function:: get_list_or_404(klass, *args, **kwargs)
Returns the result of :meth:`~django.db.models.QuerySet.filter()` on a
given model manager, raising ``django.http.Http404`` if the resulting list
is empty.
Required arguments
------------------

View File

@ -3,6 +3,8 @@ Unit tests for reverse URL lookups.
"""
from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.shortcuts import redirect
from django.test import TestCase
test_data = (
@ -97,3 +99,34 @@ class URLPatternReverse(TestCase):
else:
self.assertEquals(got, expected)
class ReverseShortcutTests(TestCase):
urls = 'regressiontests.urlpatterns_reverse.urls'
def test_redirect_to_object(self):
# We don't really need a model; just something with a get_absolute_url
class FakeObj(object):
def get_absolute_url(self):
return "/hi-there/"
res = redirect(FakeObj())
self.assert_(isinstance(res, HttpResponseRedirect))
self.assertEqual(res['Location'], '/hi-there/')
res = redirect(FakeObj(), permanent=True)
self.assert_(isinstance(res, HttpResponsePermanentRedirect))
self.assertEqual(res['Location'], '/hi-there/')
def test_redirect_to_view_name(self):
res = redirect('hardcoded2')
self.assertEqual(res['Location'], '/hardcoded/doc.pdf')
res = redirect('places', 1)
self.assertEqual(res['Location'], '/places/1/')
res = redirect('headlines', year='2008', month='02', day='17')
self.assertEqual(res['Location'], '/headlines/2008.02.17/')
self.assertRaises(NoReverseMatch, redirect, 'not-a-view')
def test_redirect_to_url(self):
res = redirect('/foo/')
self.assertEqual(res['Location'], '/foo/')
res = redirect('http://example.com/')
self.assertEqual(res['Location'], 'http://example.com/')