Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

unicode: Fixed a number of problems where lazily translated objects w…

…ere not

being converted back to unicode strings correctly. Fixed #4359 and #4361.


git-svn-id: http://code.djangoproject.com/svn/django/branches/unicode@5320 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit b25f6e9526ba3ccbdbe3c4f0d78813e9412a7acf 1 parent d72f630
@malcolmt malcolmt authored
View
2  django/contrib/admin/templatetags/admin_list.py
@@ -78,7 +78,7 @@ def result_headers(cl):
# attribute "short_description". If that doesn't exist, fall back
# to the method name. And __str__ and __unicode__ are special-cases.
if field_name == '__unicode__':
- header = smart_unicode(lookup_opts.verbose_name)
+ header = force_unicode(lookup_opts.verbose_name)
elif field_name == '__str__':
header = smart_str(lookup_opts.verbose_name)
else:
View
32 django/contrib/admin/views/main.py
@@ -12,7 +12,7 @@
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.utils.html import escape
from django.utils.text import capfirst, get_text_list
-from django.utils.encoding import smart_unicode, smart_str
+from django.utils.encoding import force_unicode, smart_str
from django.utils.translation import ugettext as _
import operator
@@ -127,11 +127,11 @@ def __init__(self, field, field_mapping, original):
if max([bool(f.errors()) for f in self.form_fields]):
classes.append('error')
if classes:
- self.cell_class_attribute = ' class="%s" ' % ' '.join(classes)
+ self.cell_class_attribute = u' class="%s" ' % ' '.join(classes)
self._repr_filled = False
if field.rel:
- self.related_url = '../../../%s/%s/' % (field.rel.to._meta.app_label, field.rel.to._meta.object_name.lower())
+ self.related_url = u'../../../%s/%s/' % (field.rel.to._meta.app_label, field.rel.to._meta.object_name.lower())
def original_value(self):
if self.original:
@@ -144,7 +144,7 @@ def existing_display(self):
if isinstance(self.field.rel, models.ManyToOneRel):
self._display = getattr(self.original, self.field.name)
elif isinstance(self.field.rel, models.ManyToManyRel):
- self._display = u", ".join([smart_unicode(obj) for obj in getattr(self.original, self.field.name).all()])
+ self._display = u", ".join([force_unicode(obj) for obj in getattr(self.original, self.field.name).all()])
return self._display
def __repr__(self):
@@ -255,7 +255,7 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
if not errors:
new_object = manipulator.save(new_data)
pk_value = new_object._get_pk_val()
- LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, smart_unicode(new_object), ADDITION)
+ LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), ADDITION)
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
@@ -268,7 +268,7 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
if type(pk_value) is str: # Quote if string, so JavaScript doesn't think it's a variable.
pk_value = '"%s"' % pk_value.replace('"', '\\"')
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \
- (pk_value, smart_unicode(new_object).replace('"', '\\"')))
+ (pk_value, force_unicode(new_object).replace('"', '\\"')))
elif "_addanother" in request.POST:
request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
return HttpResponseRedirect(request.path)
@@ -342,7 +342,7 @@ def change_stage(request, app_label, model_name, object_id):
change_message = ' '.join(change_message)
if not change_message:
change_message = _('No fields changed.')
- LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, smart_unicode(new_object), CHANGE, change_message)
+ LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), CHANGE, change_message)
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object}
if "_continue" in request.POST:
@@ -431,10 +431,10 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if related.field.rel.edit_inline or not related.opts.admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
- nh(deleted_objects, current_depth, ['%s: %s' % (capfirst(related.opts.verbose_name), sub_obj), []])
+ nh(deleted_objects, current_depth, [u'%s: %s' % (capfirst(related.opts.verbose_name), sub_obj), []])
else:
# Display a link to the admin page.
- nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
+ nh(deleted_objects, current_depth, [u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
(capfirst(related.opts.verbose_name), related.opts.app_label, related.opts.object_name.lower(),
sub_obj._get_pk_val(), sub_obj), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
@@ -445,10 +445,10 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if related.field.rel.edit_inline or not related.opts.admin:
# Don't display link to edit, because it either has no
# admin or is edited inline.
- nh(deleted_objects, current_depth, ['%s: %s' % (capfirst(related.opts.verbose_name), escape(sub_obj)), []])
+ nh(deleted_objects, current_depth, [u'%s: %s' % (capfirst(related.opts.verbose_name), escape(sub_obj)), []])
else:
# Display a link to the admin page.
- nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
+ nh(deleted_objects, current_depth, [u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
(capfirst(related.opts.verbose_name), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(sub_obj)), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
# If there were related objects, and the user doesn't have
@@ -481,12 +481,12 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
# Display a link to the admin page.
nh(deleted_objects, current_depth, [
(_('One or more %(fieldname)s in %(name)s:') % {'fieldname': related.field.verbose_name, 'name':related.opts.verbose_name}) + \
- (' <a href="../../../../%s/%s/%s/">%s</a>' % \
+ (u' <a href="../../../../%s/%s/%s/">%s</a>' % \
(related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj))), []])
# If there were related objects, and the user doesn't have
# permission to change them, add the missing perm to perms_needed.
if related.opts.admin and has_related_objs:
- p = '%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
+ p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
@@ -503,14 +503,14 @@ def delete_stage(request, app_label, model_name, object_id):
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
- deleted_objects = ['%s: <a href="../../%s/">%s</a>' % (capfirst(opts.verbose_name), object_id, escape(obj)), []]
+ deleted_objects = [u'%s: <a href="../../%s/">%s</a>' % (capfirst(opts.verbose_name), object_id, escape(obj)), []]
perms_needed = sets.Set()
_get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1)
if request.POST: # The user has already confirmed the deletion.
if perms_needed:
raise PermissionDenied
- obj_display = smart_unicode(obj)
+ obj_display = force_unicode(obj)
obj.delete()
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, object_id, obj_display, DELETION)
request.user.message_set.create(message=_('The %(name)s "%(obj)s" was deleted successfully.') % {'name': opts.verbose_name, 'obj': obj_display})
@@ -600,7 +600,7 @@ def get_query_string(self, new_params=None, remove=None):
del p[k]
elif v is not None:
p[k] = v
- return '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
+ return '?' + '&amp;'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
def get_results(self, request):
paginator = ObjectPaginator(self.query_set, self.lookup_opts.admin.list_per_page)
View
4 django/contrib/auth/models.py
@@ -3,7 +3,7 @@
from django.db import backend, connection, models
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_str
-from django.utils.translation import ugettext_lazy, ugettext as _
+from django.utils.translation import ugettext_lazy as _
import datetime
import urllib
@@ -281,7 +281,7 @@ def __init__(self):
pass
def __unicode__(self):
- return ugettext_lazy('AnonymousUser')
+ return _('AnonymousUser')
def __eq__(self, other):
return isinstance(other, self.__class__)
View
6 django/db/models/base.py
@@ -12,7 +12,7 @@
from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict
from django.utils.functional import curry
-from django.utils.encoding import smart_str
+from django.utils.encoding import smart_str, force_unicode
from django.conf import settings
from itertools import izip
import types
@@ -88,7 +88,7 @@ def __repr__(self):
def __str__(self):
if hasattr(self, '__unicode__'):
- return unicode(self).encode('utf-8')
+ return force_unicode(self).encode('utf-8')
return '%s object' % self.__class__.__name__
def __eq__(self, other):
@@ -320,7 +320,7 @@ def delete(self):
def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
- return dict(field.choices).get(value, value)
+ return force_unicode(dict(field.choices).get(value, value))
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
op = is_next and '>' or '<'
View
2  django/db/models/options.py
@@ -215,7 +215,7 @@ def __init__(self, fields=None, js=None, list_display=None, list_display_links=N
save_on_top=False, list_select_related=False, manager=None, list_per_page=100):
self.fields = fields
self.js = js or []
- self.list_display = list_display or ['__str__']
+ self.list_display = list_display or ['__unicode__']
self.list_display_links = list_display_links or []
self.list_filter = list_filter or []
self.date_hierarchy = date_hierarchy
View
6 django/template/__init__.py
@@ -58,9 +58,9 @@
from inspect import getargspec
from django.conf import settings
from django.template.context import Context, RequestContext, ContextPopException
-from django.utils.functional import curry
+from django.utils.functional import curry, Promise
from django.utils.text import smart_split
-from django.utils.encoding import smart_unicode, smart_str
+from django.utils.encoding import smart_unicode, smart_str, force_unicode
from django.utils.translation import ugettext as _
__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
@@ -705,6 +705,8 @@ def resolve_variable(path, context):
else:
raise
del bits[0]
+ if isinstance(current, (basestring, Promise)):
+ current = force_unicode(current)
return current
class Node(object):
View
4 tests/modeltests/choices/models.py
@@ -33,7 +33,7 @@ def __str__(self):
>>> s.gender
'F'
>>> a.get_gender_display()
-'Male'
+u'Male'
>>> s.get_gender_display()
-'Female'
+u'Female'
"""}
View
57 tests/regressiontests/invalid_admin_options/models.py
@@ -123,6 +123,9 @@ class ListFilterBadOne(models.Model):
class Admin:
list_filter = 'first_name'
+ def __unicode__(self):
+ return self.first_name
+
model_errors += """invalid_admin_options.listfilterbadone: "admin.list_filter", if given, must be set to a list or tuple.
"""
@@ -140,6 +143,9 @@ def full_name(self):
class Admin:
list_filter = ['first_name','last_name','full_name']
+ def __unicode__(self):
+ return self.first_name
+
model_errors += """invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'last_name', which isn't a field.
invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'full_name', which isn't a field.
"""
@@ -151,7 +157,10 @@ class DateHierarchyBadOne(models.Model):
class Admin:
date_hierarchy = 'first_name'
-
+
+ def __unicode__(self):
+ return self.first_name
+
# TODO: Date Hierarchy needs to check if field is a date/datetime field.
#model_errors += """invalid_admin_options.datehierarchybadone: "admin.date_hierarchy" refers to 'first_name', which isn't a date field or datetime field.
#"""
@@ -164,6 +173,9 @@ class DateHierarchyBadTwo(models.Model):
class Admin:
date_hierarchy = 'nonexistent'
+ def __unicode__(self):
+ return self.first_name
+
model_errors += """invalid_admin_options.datehierarchybadtwo: "admin.date_hierarchy" refers to 'nonexistent', which isn't a field.
"""
@@ -174,6 +186,9 @@ class DateHierarchyGood(models.Model):
class Admin:
date_hierarchy = 'birth_day'
+
+ def __unicode__(self):
+ return self.first_name
class SearchFieldsBadOne(models.Model):
"Test search_fields, must be a list or tuple."
@@ -182,6 +197,9 @@ class SearchFieldsBadOne(models.Model):
class Admin:
search_fields = ('nonexistent')
+ def __unicode__(self):
+ return self.first_name
+
# TODO: Add search_fields validation
#model_errors += """invalid_admin_options.seacrhfieldsbadone: "admin.search_fields", if given, must be set to a list or tuple.
#"""
@@ -197,6 +215,9 @@ def _last_name(self):
class Admin:
search_fields = ['first_name','last_name']
+ def __unicode__(self):
+ return self.first_name
+
# TODO: Add search_fields validation
#model_errors += """invalid_admin_options.seacrhfieldsbadone: "admin.search_fields" refers to 'last_name', which isn't a field.
#"""
@@ -209,6 +230,8 @@ class SearchFieldsGood(models.Model):
class Admin:
search_fields = ['first_name','last_name']
+ def __unicode__(self):
+ return self.first_name
class JsBadOne(models.Model):
"Test js, must be a list or tuple"
@@ -216,6 +239,9 @@ class JsBadOne(models.Model):
class Admin:
js = 'test.js'
+
+ def __unicode__(self):
+ return self.name
# TODO: Add a js validator
#model_errors += """invalid_admin_options.jsbadone: "admin.js", if given, must be set to a list or tuple.
@@ -228,6 +254,9 @@ class SaveAsBad(models.Model):
class Admin:
save_as = 'not True or False'
+ def __unicode__(self):
+ return self.name
+
# TODO: Add a save_as validator.
#model_errors += """invalid_admin_options.saveasbad: "admin.save_as", if given, must be set to True or False.
#"""
@@ -239,6 +268,9 @@ class SaveOnTopBad(models.Model):
class Admin:
save_on_top = 'not True or False'
+ def __unicode__(self):
+ return self.name
+
# TODO: Add a save_on_top validator.
#model_errors += """invalid_admin_options.saveontopbad: "admin.save_on_top", if given, must be set to True or False.
#"""
@@ -250,6 +282,9 @@ class ListSelectRelatedBad(models.Model):
class Admin:
list_select_related = 'not True or False'
+ def __unicode__(self):
+ return self.name
+
# TODO: Add a list_select_related validator.
#model_errors += """invalid_admin_options.listselectrelatebad: "admin.list_select_related", if given, must be set to True or False.
#"""
@@ -261,6 +296,9 @@ class ListPerPageBad(models.Model):
class Admin:
list_per_page = 89.3
+ def __unicode__(self):
+ return self.name
+
# TODO: Add a list_per_page validator.
#model_errors += """invalid_admin_options.listperpagebad: "admin.list_per_page", if given, must be a positive integer.
#"""
@@ -273,6 +311,9 @@ class FieldsBadOne(models.Model):
class Admin:
fields = 'not a tuple'
+ def __unicode__(self):
+ return self.first_name
+
# TODO: Add a fields validator.
#model_errors += """invalid_admin_options.fieldsbadone: "admin.fields", if given, must be a tuple.
#"""
@@ -284,6 +325,9 @@ class FieldsBadTwo(models.Model):
class Admin:
fields = ('Name', {'description': 'this fieldset needs fields'})
+
+ def __unicode__(self):
+ return self.first_name
# TODO: Add a fields validator.
#model_errors += """invalid_admin_options.fieldsbadtwo: "admin.fields" each fieldset must include a 'fields' dict.
@@ -297,6 +341,9 @@ class FieldsBadThree(models.Model):
class Admin:
fields = ('Name', {'fields': ('first_name','last_name'),'badoption': 'verybadoption'})
+ def __unicode__(self):
+ return self.first_name
+
# TODO: Add a fields validator.
#model_errors += """invalid_admin_options.fieldsbadthree: "admin.fields" fieldset options must be either 'classes' or 'description'.
#"""
@@ -312,6 +359,9 @@ class Admin:
('Name', {'fields': ('first_name','last_name'),'classes': 'collapse'}),
(None, {'fields': ('birth_day',),'description': 'enter your b-day'})
)
+
+ def __unicode__(self):
+ return self.first_name
class OrderingBad(models.Model):
"Test ordering, must be a field."
@@ -321,6 +371,9 @@ class OrderingBad(models.Model):
class Admin:
ordering = 'nonexistent'
+ def __unicode__(self):
+ return self.first_name
+
# TODO: Add a ordering validator.
#model_errors += """invalid_admin_options.orderingbad: "admin.ordering" refers to 'nonexistent', which isn't a field.
#"""
@@ -334,4 +387,4 @@ class Admin:
# manager = 'nonexistent'
#
#model_errors += """invalid_admin_options.managerbad: "admin.manager" refers to 'nonexistent', which isn't a Manager().
-#"""
+#"""
Please sign in to comment.
Something went wrong with that request. Please try again.