{% endif %}
Index: django/utils/http.py
===================================================================
--- django/utils/http.py (revision 7894)
+++ django/utils/http.py (working copy)
@@ -4,6 +4,11 @@
from django.utils.encoding import smart_str, force_unicode
from django.utils.functional import allow_lazy
+# These are the safe characters as defined by:
+# http://www.ietf.org/rfc/rfc2396.txt (see: 2.3. Unreserved Characters)
+# these characters are safe to be used in a URL part.
+URL_SAFE_CHARACTERS = "-_.!~*'()"
+
def urlquote(url, safe='/'):
"""
A version of Python's urllib.quote() function that can operate on unicode
Index: tests/regressiontests/admin_views/fixtures/string-primary-key.xml
===================================================================
--- tests/regressiontests/admin_views/fixtures/string-primary-key.xml (revision 0)
+++ tests/regressiontests/admin_views/fixtures/string-primary-key.xml (revision 0)
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
Index: tests/regressiontests/admin_views/tests.py
===================================================================
--- tests/regressiontests/admin_views/tests.py (revision 7894)
+++ tests/regressiontests/admin_views/tests.py (working copy)
@@ -2,10 +2,11 @@
from django.test import TestCase
from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType
+from django.contrib.admin.models import LogEntry
from django.contrib.admin.sites import LOGIN_FORM_KEY, _encode_post_data
# local test models
-from models import Article, CustomArticle, Section
+from models import Article, CustomArticle, Section, ModelWithStringPrimaryKey
def get_perm(Model, perm):
"""Return the permission object, for the Model"""
@@ -320,4 +321,60 @@
self.assertRedirects(post, '/test_admin/admin/')
self.failUnlessEqual(Article.objects.all().count(), 0)
self.client.get('/test_admin/admin/logout/')
-
\ No newline at end of file
+
+class AdminViewStringPrimaryKeyTest(TestCase):
+ fixtures = ['admin-views-users.xml', 'string-primary-key.xml']
+
+ def __init__(self, *args):
+ super(AdminViewStringPrimaryKeyTest, self).__init__(*args)
+
+ # The primary key value used is just a string containing all possible characters
+ self.pk = """abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`"""
+
+ self.pk_escaped = """abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`"""
+
+ # The characters that should be quoted are defined in:
+ # These are the safe characters as defined by:
+ # http://www.ietf.org/rfc/rfc2396.txt (see chapter 2)
+ # The safe characters are defined in django.util.http.URL_SAFE_CHARACTERS
+ self.pk_quoted = """abcdefghijklmnopqrstuvwxyz%20ABCDEFGHIJKLMNOPQRSTUVWXYZ%201234567890%20-_.!~*'()%20%3B%2F%3F%3A%40%26%3D%2B%24%2C%20%3C%3E%23%25%22%20%7B%7D%7C%5C%5E%5B%5D%60"""
+
+ def setUp(self):
+ self.client.login(username='super', password='secret')
+
+ # This entry refers to content type which is not known at the moment the fixture is imported
+ content_type_id = ContentType.objects.get_for_model(ModelWithStringPrimaryKey).id
+ ContentType.objects.all()
+
+ LogEntry.objects.log_action(100, content_type_id, self.pk, self.pk, 2, change_message='')
+
+ def tearDown(self):
+ self.client.logout()
+
+ def test_get_change_view(self):
+ "Retrieving the object using urlencoded form of primary key should work"
+ response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % self.pk_quoted)
+
+ self.assertContains(response, self.pk_escaped)
+
+ def test_changelist_to_changeform_link(self):
+ "The link from the changelist referring to the changeform of the object should be quoted"
+ response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/')
+
+ should_contain = """
""" % (self.pk_quoted, self.pk_escaped)
+ self.assertContains(response, should_contain)
+
+ def test_recentactions_link(self):
+ "The link from the recent actions list referring to the changeform of the object should be quoted"
+ response = self.client.get('/test_admin/admin/')
+
+ should_contain = """%s""" % (self.pk_quoted, self.pk_escaped)
+ self.assertContains(response, should_contain)
+
+ def test_deleteconfirmation_link(self):
+ "The link from the delete confirmation page referring back to the changeform of the object should be quoted"
+ response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/delete/' % self.pk_quoted)
+
+ should_contain = """%s""" % (self.pk_quoted, self.pk_escaped)
+ self.assertContains(response, should_contain)
+
\ No newline at end of file
Index: tests/regressiontests/admin_views/models.py
===================================================================
--- tests/regressiontests/admin_views/models.py (revision 7894)
+++ tests/regressiontests/admin_views/models.py (working copy)
@@ -48,7 +48,14 @@
'extra_var': 'Hello!'
}
)
-
+
+class ModelWithStringPrimaryKey(models.Model):
+ id = models.CharField(max_length='255', primary_key=True)
+
+ def __unicode__(self):
+ return self.id
+
admin.site.register(Article, ArticleAdmin)
admin.site.register(CustomArticle, CustomArticleAdmin)
admin.site.register(Section)
+admin.site.register(ModelWithStringPrimaryKey)