diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py index 1a5fa86481..fa4fb76714 100644 --- a/django/contrib/databrowse/datastructures.py +++ b/django/contrib/databrowse/datastructures.py @@ -3,12 +3,11 @@ These classes are light wrappers around Django's database API that provide convenience functionality and permalink functions for the databrowse app. """ -import urllib from django.db import models from django.utils import dateformat from django.utils.text import capfirst from django.utils.translation import get_date_formats -from django.utils.encoding import smart_unicode, smart_str +from django.utils.encoding import smart_unicode, smart_str, iri_to_uri EMPTY_VALUE = '(None)' @@ -77,7 +76,7 @@ class EasyChoice(object): return smart_str(u'' % (self.model.model._meta.object_name, self.field.name)) def url(self): - return '%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, urllib.quote(smart_str(self.value))) + return '%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value)) class EasyInstance(object): def __init__(self, easy_model, instance): @@ -99,7 +98,7 @@ class EasyInstance(object): return self.instance._get_pk_val() def url(self): - return '%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, urllib.quote(smart_str(self.pk()))) + return '%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, iri_to_uri(self.pk())) def fields(self): """ @@ -180,18 +179,18 @@ class EasyInstanceField(object): if self.field.rel.to in self.model.model_list: lst = [] for value in self.values(): - url = '%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, urllib.quote(smart_str(value._get_pk_val()))) + url = '%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val())) lst.append((smart_unicode(value), url)) else: lst = [(value, None) for value in self.values()] elif self.field.choices: lst = [] for value in self.values(): - url = '%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, urllib.quote(smart_str(self.raw_value))) + url = '%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, iri_to_uri(self.raw_value)) lst.append((value, url)) elif isinstance(self.field, models.URLField): val = self.values()[0] - lst = [(val, urllib.quote(smart_str(val)))] + lst = [(val, iri_to_uri(val))] else: lst = [(self.values()[0], None)] return lst diff --git a/django/utils/encoding.py b/django/utils/encoding.py index d77269bde8..7466e02300 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -1,7 +1,17 @@ import types +import urllib from django.conf import settings from django.utils.functional import Promise +class StrAndUnicode(object): + """ + A class whose __str__ returns its __unicode__ as a UTF-8 bytestring. + + Useful as a mix-in. + """ + def __str__(self): + return self.__unicode__().encode('utf-8') + def smart_unicode(s, encoding='utf-8', errors='strict'): """ Returns a unicode object representing 's'. Treats bytestrings using the @@ -45,12 +55,16 @@ def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): else: return s -class StrAndUnicode(object): +def iri_to_uri(iri): """ - A class whose __str__ returns its __unicode__ as a UTF-8 bytestring. + Convert an Internationalized Resource Identifier (IRI) portion to a URI + portion that is suitable for inclusion in a URL. - Useful as a mix-in. + This is the algorithm from section 3.1 of RFC 3987. However, since we are + assuming input is either UTF-8 or unicode already, we can simplify things a + little from the full method. + + Returns an ASCII string containing the encoded result. """ - def __str__(self): - return self.__unicode__().encode('utf-8') + return urllib.quote(smart_str(iri), safe='/#%[]') diff --git a/tests/regressiontests/text/tests.py b/tests/regressiontests/text/tests.py index e88cadba46..c57ac54078 100644 --- a/tests/regressiontests/text/tests.py +++ b/tests/regressiontests/text/tests.py @@ -1,5 +1,6 @@ -""" -# Tests for stuff in django.utils.text. +# coding: utf-8 +r""" +# Tests for stuff in django.utils.text and other text munging util functions. >>> from django.utils.text import * @@ -8,10 +9,17 @@ [u'This', u'is', u'"a person"', u'test.'] >>> print list(smart_split(r'''This is "a person's" test.'''))[2] "a person's" ->>> print list(smart_split(r'''This is "a person\\"s" test.'''))[2] +>>> print list(smart_split(r'''This is "a person\"s" test.'''))[2] "a person"s" >>> list(smart_split('''"a 'one''')) [u'"a', u"'one"] >>> print list(smart_split(r'''all friends' tests'''))[1] friends' + +### iri_to_uri ########################################################### +>>> from django.utils.encoding import iri_to_uri +>>> iri_to_uri(u'red%09ros\xe9#red') +'red%09ros%C3%A9#red' +>>> iri_to_uri(u'/blog/for/J\xfcrgen M\xfcnster/') +'/blog/for/J%C3%BCrgen%20M%C3%BCnster/' """