diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py
index 88867d5..014c819 100644
--- a/django/contrib/auth/tests/views.py
+++ b/django/contrib/auth/tests/views.py
@@ -265,7 +265,7 @@ class LoginURLSettings(AuthViewsTestCase):
         querystring = QueryDict('', mutable=True)
         querystring['next'] = '/login_required/'
         self.assertEqual(login_required_url,
-             'http://testserver%s?%s' % (login_url, querystring.urlencode()))
+             'http://testserver%s?%s' % (login_url, querystring.urlencode('/')))
 
     def test_remote_login_url(self):
         login_url = 'http://remote.example.com/login'
@@ -273,7 +273,7 @@ class LoginURLSettings(AuthViewsTestCase):
         querystring = QueryDict('', mutable=True)
         querystring['next'] = 'http://testserver/login_required/'
         self.assertEqual(login_required_url,
-                         '%s?%s' % (login_url, querystring.urlencode()))
+                         '%s?%s' % (login_url, querystring.urlencode('/')))
 
     def test_https_login_url(self):
         login_url = 'https:///login/'
@@ -281,7 +281,7 @@ class LoginURLSettings(AuthViewsTestCase):
         querystring = QueryDict('', mutable=True)
         querystring['next'] = 'http://testserver/login_required/'
         self.assertEqual(login_required_url,
-                         '%s?%s' % (login_url, querystring.urlencode()))
+                         '%s?%s' % (login_url, querystring.urlencode('/')))
 
     def test_login_url_with_querystring(self):
         login_url = '/login/?pretty=1'
@@ -289,7 +289,7 @@ class LoginURLSettings(AuthViewsTestCase):
         querystring = QueryDict('pretty=1', mutable=True)
         querystring['next'] = '/login_required/'
         self.assertEqual(login_required_url, 'http://testserver/login/?%s' %
-                         querystring.urlencode())
+                         querystring.urlencode('/'))
 
     def test_remote_login_url_with_next_querystring(self):
         login_url = 'http://remote.example.com/login/'
@@ -298,8 +298,9 @@ class LoginURLSettings(AuthViewsTestCase):
         querystring = QueryDict('', mutable=True)
         querystring['next'] = 'http://testserver/login_required/'
         self.assertEqual(login_required_url, '%s?%s' % (login_url,
-                                                    querystring.urlencode()))
-        
+                                                    querystring.urlencode('/')))
+
+
 class LogoutTest(AuthViewsTestCase):
     urls = 'django.contrib.auth.tests.urls'
 
diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index d1f0e06..06bf8f4 100644
--- a/django/contrib/auth/views.py
+++ b/django/contrib/auth/views.py
@@ -108,7 +108,7 @@ def redirect_to_login(next, login_url=None,
     if redirect_field_name:
         querystring = QueryDict(login_url_parts[4], mutable=True)
         querystring[redirect_field_name] = next
-        login_url_parts[4] = querystring.urlencode()
+        login_url_parts[4] = querystring.urlencode(safe='/')
 
     return HttpResponseRedirect(urlparse.urlunparse(login_url_parts))
 
diff --git a/django/http/__init__.py b/django/http/__init__.py
index 42027f0..6526c63 100644
--- a/django/http/__init__.py
+++ b/django/http/__init__.py
@@ -3,7 +3,7 @@ import os
 import re
 import time
 from pprint import pformat
-from urllib import urlencode
+from urllib import urlencode, quote
 from urlparse import urljoin
 try:
     from cStringIO import StringIO
@@ -363,11 +363,28 @@ class QueryDict(MultiValueDict):
         """Returns a mutable copy of this object."""
         return self.__deepcopy__({})
 
-    def urlencode(self):
+    def urlencode(self, safe=None):
+        """
+        Returns an encoded string of all query string arguments.
+
+        :arg safe: Used to specify characters which do not require quoting, e.g.::
+
+            >>> q = QueryDict('', mutable=True)
+            >>> q['next'] = '/a&b/'
+            >>> q.urlencode()
+            'next=%2Fa%26b%2F'
+            >>> q.urlencode(safe='/')
+            'next=/a%26b/'
+
+        """
         output = []
+        if safe:
+            encode = lambda k, v: u'%s=%s' % ((quote(k, safe), quote(v, safe)))
+        else:
+            encode = lambda k, v: urlencode({k: v})
         for k, list_ in self.lists():
             k = smart_str(k, self.encoding)
-            output.extend([urlencode({k: smart_str(v, self.encoding)}) for v in list_])
+            output.extend([encode(k, smart_str(v, self.encoding)) for v in list_])
         return '&'.join(output)
 
 class CompatCookie(SimpleCookie):
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index cc89229..036cb8d 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -430,14 +430,26 @@ In addition, ``QueryDict`` has the following methods:
     Like :meth:`items()`, except it includes all values, as a list, for each
     member of the dictionary. For example::
 
-         >>> q = QueryDict('a=1&a=2&a=3')
-         >>> q.lists()
-         [(u'a', [u'1', u'2', u'3'])]
+        >>> q = QueryDict('a=1&a=2&a=3')
+        >>> q.lists()
+        [(u'a', [u'1', u'2', u'3'])]
 
-.. method:: QueryDict.urlencode()
+.. method:: QueryDict.urlencode([safe])
 
-    Returns a string of the data in query-string format.
-    Example: ``"a=2&b=3&b=5"``.
+    Returns a string of the data in query-string format. Example::
+
+        >>> q = QueryDict('a=2&b=3&b=5')
+        >>> q.urlencode()
+        'a=2&b=3&b=5'
+
+    .. versionadded:: 1.3
+
+    Optionally, urlencode can be passed characters which
+    do not require encoding. For example::
+
+        >>> q = QueryDict('/a&b/')
+        >>> q.urlencode(safe='/')
+        'next=/a%26b/'
 
 HttpResponse objects
 ====================
diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py
index 6d45252..65b06a1 100644
--- a/tests/regressiontests/httpwrappers/tests.py
+++ b/tests/regressiontests/httpwrappers/tests.py
@@ -71,6 +71,16 @@ class QueryDictTests(unittest.TestCase):
 
         self.assertEqual(q.urlencode(), 'foo=bar')
 
+    def test_urlencode(self):
+        q = QueryDict('', mutable=True)
+        q['next'] = '/a&b/'
+        self.assertEqual(q.urlencode(), 'next=%2Fa%26b%2F')
+        self.assertEqual(q.urlencode(safe='/'), 'next=/a%26b/')
+        q = QueryDict('', mutable=True)
+        q['next'] = u'/t\xebst&key/'
+        self.assertEqual(q.urlencode(), 'next=%2Ft%C3%ABst%26key%2F')
+        self.assertEqual(q.urlencode(safe='/'), 'next=/t%C3%ABst%26key/')
+
     def test_mutable_copy(self):
         """A copy of a QueryDict is mutable."""
         q = QueryDict('').copy()
