diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 2e5e6b87fb..8429018318 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/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: diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 716201b66a..4d350fb388 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -12,7 +12,7 @@ from django.db.models.query import handle_legacy_orderlist, QuerySet 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 @@ class AdminBoundField(object): 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 @@ class AdminBoundField(object): 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('' % \ - (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: %s' % \ + nh(deleted_objects, current_depth, [u'%s: %s' % \ (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: %s' % \ + nh(deleted_objects, current_depth, [u'%s: %s' % \ (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}) + \ - (' %s' % \ + (u' %s' % \ (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: %s' % (capfirst(opts.verbose_name), object_id, escape(obj)), []] + deleted_objects = [u'%s: %s' % (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 @@ class ChangeList(object): del p[k] elif v is not None: p[k] = v - return '?' + '&'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20') + return '?' + '&'.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) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 38d30b7afb..dfe642d818 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -3,7 +3,7 @@ from django.core.exceptions import ImproperlyConfigured 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 @@ class AnonymousUser(object): pass def __unicode__(self): - return ugettext_lazy('AnonymousUser') + return _('AnonymousUser') def __eq__(self, other): return isinstance(other, self.__class__) diff --git a/django/db/models/base.py b/django/db/models/base.py index 99e57e44c6..8d0d216b0c 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -12,7 +12,7 @@ from django.db.models.loading import register_models, get_model 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 @@ class Model(object): 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 @@ class Model(object): 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 '<' diff --git a/django/db/models/options.py b/django/db/models/options.py index 21fe4a39d3..f3768cfe16 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -215,7 +215,7 @@ class AdminOptions(object): 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 diff --git a/django/template/__init__.py b/django/template/__init__.py index 97a3c9d182..81cd75d804 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -58,9 +58,9 @@ import re 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): diff --git a/tests/modeltests/choices/models.py b/tests/modeltests/choices/models.py index 37d36fe1d8..e287d973bc 100644 --- a/tests/modeltests/choices/models.py +++ b/tests/modeltests/choices/models.py @@ -33,7 +33,7 @@ __test__ = {'API_TESTS':""" >>> s.gender 'F' >>> a.get_gender_display() -'Male' +u'Male' >>> s.get_gender_display() -'Female' +u'Female' """} diff --git a/tests/regressiontests/invalid_admin_options/models.py b/tests/regressiontests/invalid_admin_options/models.py index 43bcc533ba..d3158edf38 100644 --- a/tests/regressiontests/invalid_admin_options/models.py +++ b/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 @@ class ListFilterBadTwo(models.Model): 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 @@ class SearchFieldsBadTwo(models.Model): 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 FieldsGood(models.Model): ('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 OrderingBad(models.Model): # manager = 'nonexistent' # #model_errors += """invalid_admin_options.managerbad: "admin.manager" refers to 'nonexistent', which isn't a Manager(). -#""" \ No newline at end of file +#"""