Ticket #11688: 11688-verbose_name_plural_evolution-2.diff
File 11688-verbose_name_plural_evolution-2.diff, 85.1 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/actions.py
diff --git a/django/contrib/admin/actions.py b/django/contrib/admin/actions.py
a b 20 20 21 21 Next, it delets all selected objects and redirects back to the change list. 22 22 """ 23 opts = modeladmin.model._meta 23 model = modeladmin.model 24 opts = model._meta 24 25 app_label = opts.app_label 25 26 26 27 # Check that the user has delete permission for the actual model 27 28 if not modeladmin.has_delete_permission(request): 28 29 raise PermissionDenied 29 30 30 using = router.db_for_write(model admin.model)31 using = router.db_for_write(model) 31 32 32 33 # Populate deletable_objects, a data structure of all related objects that 33 34 # will also be deleted. … … 46 47 modeladmin.log_deletion(request, obj, obj_display) 47 48 queryset.delete() 48 49 modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % { 49 "count": n, "items": model_ngettext(model admin.opts, n)50 "count": n, "items": model_ngettext(model, n) 50 51 }) 51 52 # Return None to display the change list page again. 52 53 return None 53 54 54 if len(queryset) == 1: 55 objects_name = force_unicode(opts.verbose_name) 56 else: 57 objects_name = force_unicode(opts.verbose_name_plural) 55 objects_name = force_unicode(opts.get_verbose_name(len(queryset))) 58 56 59 57 if perms_needed or protected: 60 58 title = _("Cannot delete %(name)s") % {"name": objects_name} -
django/contrib/admin/filters.py
diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py
a b 150 150 if hasattr(field, 'verbose_name'): 151 151 self.lookup_title = field.verbose_name 152 152 else: 153 self.lookup_title = other_model._meta. verbose_name153 self.lookup_title = other_model._meta.get_verbose_name() 154 154 rel_name = other_model._meta.pk.name 155 155 self.lookup_kwarg = '%s__%s__exact' % (self.field_path, rel_name) 156 156 self.lookup_kwarg_isnull = '%s__isnull' % (self.field_path) -
django/contrib/admin/models.py
diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py
a b 2 2 from django.contrib.contenttypes.models import ContentType 3 3 from django.contrib.auth.models import User 4 4 from django.contrib.admin.util import quote 5 from django.utils.translation import ugettext_lazy as _ 5 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 6 6 from django.utils.encoding import smart_unicode 7 7 from django.utils.safestring import mark_safe 8 8 … … 27 27 objects = LogEntryManager() 28 28 29 29 class Meta: 30 verbose_name = _('log entry')31 verbose_name_plural = _('log entries')32 30 db_table = 'django_admin_log' 33 31 ordering = ('-action_time',) 34 32 … … 66 64 if self.content_type and self.object_id: 67 65 return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id))) 68 66 return None 67 68 @classmethod 69 def verbose_names(cls, count=1): 70 return ungettext_lazy('log entry', 'log entries', count) -
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
a b 609 609 """ 610 610 choices = [] + default_choices 611 611 for func, name, description in self.get_actions(request).itervalues(): 612 choice = (name, description % model_format_dict(self. opts))612 choice = (name, description % model_format_dict(self.model)) 613 613 choices.append(choice) 614 614 return choices 615 615 … … 674 674 for formset in formsets: 675 675 for added_object in formset.new_objects: 676 676 change_message.append(_('Added %(name)s "%(object)s".') 677 % {'name': force_unicode(added_object._meta. verbose_name),677 % {'name': force_unicode(added_object._meta.get_verbose_name()), 678 678 'object': force_unicode(added_object)}) 679 679 for changed_object, changed_fields in formset.changed_objects: 680 680 change_message.append(_('Changed %(list)s for %(name)s "%(object)s".') 681 681 % {'list': get_text_list(changed_fields, _('and')), 682 'name': force_unicode(changed_object._meta. verbose_name),682 'name': force_unicode(changed_object._meta.get_verbose_name()), 683 683 'object': force_unicode(changed_object)}) 684 684 for deleted_object in formset.deleted_objects: 685 685 change_message.append(_('Deleted %(name)s "%(object)s".') 686 % {'name': force_unicode(deleted_object._meta. verbose_name),686 % {'name': force_unicode(deleted_object._meta.get_verbose_name()), 687 687 'object': force_unicode(deleted_object)}) 688 688 change_message = ' '.join(change_message) 689 689 return change_message or _('No fields changed.') … … 769 769 opts = obj._meta 770 770 pk_value = obj._get_pk_val() 771 771 772 msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts. verbose_name), 'obj': force_unicode(obj)}772 msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.get_verbose_name()), 'obj': force_unicode(obj)} 773 773 # Here, we distinguish between different save types by checking for 774 774 # the presence of keys in request.POST. 775 775 if "_continue" in request.POST: … … 785 785 # escape() calls force_unicode. 786 786 (escape(pk_value), escapejs(obj))) 787 787 elif "_addanother" in request.POST: 788 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts. verbose_name)))788 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.get_verbose_name()))) 789 789 return HttpResponseRedirect(request.path) 790 790 else: 791 791 self.message_user(request, msg) … … 810 810 811 811 # Handle proxy models automatically created by .only() or .defer(). 812 812 # Refs #14529 813 verbose_name = opts. verbose_name813 verbose_name = opts.get_verbose_name() 814 814 module_name = opts.module_name 815 815 if obj._deferred: 816 816 opts_ = opts.proxy_for_model._meta 817 verbose_name = opts_. verbose_name817 verbose_name = opts_.get_verbose_name() 818 818 module_name = opts_.module_name 819 819 820 820 pk_value = obj._get_pk_val() … … 995 995 media = media + inline_admin_formset.media 996 996 997 997 context = { 998 'title': _('Add %s') % force_unicode(opts. verbose_name),998 'title': _('Add %s') % force_unicode(opts.get_verbose_name()), 999 999 'adminform': adminForm, 1000 1000 'is_popup': "_popup" in request.REQUEST, 1001 1001 'show_delete': False, … … 1020 1020 raise PermissionDenied 1021 1021 1022 1022 if obj is None: 1023 raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts. verbose_name), 'key': escape(object_id)})1023 raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.get_verbose_name()), 'key': escape(object_id)}) 1024 1024 1025 1025 if request.method == 'POST' and "_saveasnew" in request.POST: 1026 1026 return self.add_view(request, form_url=reverse('admin:%s_%s_add' % … … 1086 1086 media = media + inline_admin_formset.media 1087 1087 1088 1088 context = { 1089 'title': _('Change %s') % force_unicode(opts. verbose_name),1089 'title': _('Change %s') % force_unicode(opts.get_verbose_name()), 1090 1090 'adminform': adminForm, 1091 1091 'object_id': object_id, 1092 1092 'original': obj, … … 1194 1194 changecount += 1 1195 1195 1196 1196 if changecount: 1197 if changecount == 1: 1198 name = force_unicode(opts.verbose_name) 1199 else: 1200 name = force_unicode(opts.verbose_name_plural) 1197 name = force_unicode(opts.get_verbose_name(changecount)) 1201 1198 msg = ungettext("%(count)s %(name)s was changed successfully.", 1202 1199 "%(count)s %(name)s were changed successfully.", 1203 1200 changecount) % {'count': changecount, 1204 'name': name, 1205 'obj': force_unicode(obj)} 1201 'name': name} 1206 1202 self.message_user(request, msg) 1207 1203 1208 1204 return HttpResponseRedirect(request.get_full_path()) … … 1229 1225 'All %(total_count)s selected', cl.result_count) 1230 1226 1231 1227 context = { 1232 'module_name': force_unicode(opts. verbose_name_plural),1228 'module_name': force_unicode(opts.get_verbose_name(0)), 1233 1229 'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)}, 1234 1230 'selection_note_all': selection_note_all % {'total_count': cl.result_count}, 1235 1231 'title': cl.title, … … 1255 1251 @transaction.commit_on_success 1256 1252 def delete_view(self, request, object_id, extra_context=None): 1257 1253 "The 'delete' admin view for this model." 1258 opts = self.model._meta 1254 model = self.model 1255 opts = model._meta 1259 1256 app_label = opts.app_label 1260 1257 1261 1258 obj = self.get_object(request, unquote(object_id)) … … 1264 1261 raise PermissionDenied 1265 1262 1266 1263 if obj is None: 1267 raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts. verbose_name), 'key': escape(object_id)})1264 raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.get_verbose_name()), 'key': escape(object_id)}) 1268 1265 1269 using = router.db_for_write( self.model)1266 using = router.db_for_write(model) 1270 1267 1271 1268 # Populate deleted_objects, a data structure of all related objects that 1272 1269 # will also be deleted. … … 1280 1277 self.log_deletion(request, obj, obj_display) 1281 1278 self.delete_model(request, obj) 1282 1279 1283 self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts. verbose_name), 'obj': force_unicode(obj_display)})1280 self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.get_verbose_name()), 'obj': force_unicode(obj_display)}) 1284 1281 1285 1282 if not self.has_change_permission(request, None): 1286 1283 return HttpResponseRedirect(reverse('admin:index', … … 1289 1286 (opts.app_label, opts.module_name), 1290 1287 current_app=self.admin_site.name)) 1291 1288 1292 object_name = force_unicode(opts. verbose_name)1289 object_name = force_unicode(opts.get_verbose_name()) 1293 1290 1294 1291 if perms_needed or protected: 1295 1292 title = _("Cannot delete %(name)s") % {"name": object_name} … … 1329 1326 context = { 1330 1327 'title': _('Change history: %s') % force_unicode(obj), 1331 1328 'action_list': action_list, 1332 'module_name': capfirst(force_unicode(opts. verbose_name_plural)),1329 'module_name': capfirst(force_unicode(opts.get_verbose_name(0))), 1333 1330 'object': obj, 1334 1331 'app_label': app_label, 1335 1332 'opts': opts, … … 1365 1362 self.opts = self.model._meta 1366 1363 super(InlineModelAdmin, self).__init__() 1367 1364 if self.verbose_name is None: 1368 self.verbose_name = self.model._meta. verbose_name1365 self.verbose_name = self.model._meta.get_verbose_name() 1369 1366 if self.verbose_name_plural is None: 1370 self.verbose_name_plural = self.model._meta. verbose_name_plural1367 self.verbose_name_plural = self.model._meta.get_verbose_name(0) 1371 1368 1372 1369 @property 1373 1370 def media(self): -
django/contrib/admin/sites.py
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
a b 341 341 if True in perms.values(): 342 342 info = (app_label, model._meta.module_name) 343 343 model_dict = { 344 'name': capfirst(model._meta. verbose_name_plural),344 'name': capfirst(model._meta.get_verbose_name(0)), 345 345 'admin_url': reverse('admin:%s_%s_changelist' % info, current_app=self.name), 346 346 'add_url': reverse('admin:%s_%s_add' % info, current_app=self.name), 347 347 'perms': perms, … … 387 387 if True in perms.values(): 388 388 info = (app_label, model._meta.module_name) 389 389 model_dict = { 390 'name': capfirst(model._meta. verbose_name_plural),390 'name': capfirst(model._meta.get_verbose_name(0)), 391 391 'admin_url': reverse('admin:%s_%s_changelist' % info, current_app=self.name), 392 392 'add_url': reverse('admin:%s_%s_add' % info, current_app=self.name), 393 393 'perms': perms, -
django/contrib/admin/util.py
diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
a b 90 90 p = '%s.%s' % (opts.app_label, 91 91 opts.get_delete_permission()) 92 92 if not user.has_perm(p): 93 perms_needed.add(opts. verbose_name)93 perms_needed.add(opts.get_verbose_name()) 94 94 # Display a link to the admin page. 95 95 return mark_safe(u'%s: <a href="%s">%s</a>' % 96 (escape(capfirst(opts. verbose_name)),96 (escape(capfirst(opts.get_verbose_name())), 97 97 admin_url, 98 98 escape(obj))) 99 99 else: 100 100 # Don't display link to edit, because it either has no 101 101 # admin or is edited inline. 102 return u'%s: %s' % (capfirst(opts. verbose_name),102 return u'%s: %s' % (capfirst(opts.get_verbose_name()), 103 103 force_unicode(obj)) 104 104 105 105 to_delete = collector.nested(format_callback) … … 165 165 Return a `dict` with keys 'verbose_name' and 'verbose_name_plural', 166 166 typically for use with string formatting. 167 167 168 `obj` may be a `Model` instance, `Model` subclass, or `QuerySet` instance. 168 `obj` may be a `Model` instance, `Model` subclass, a `QuerySet` instance, or 169 a `django.db.models.Options` instance. 169 170 170 171 """ 171 172 if isinstance(obj, (models.Model, models.base.ModelBase)): … … 175 176 else: 176 177 opts = obj 177 178 return { 178 'verbose_name': force_unicode(opts. verbose_name),179 'verbose_name_plural': force_unicode(opts. verbose_name_plural)179 'verbose_name': force_unicode(opts.get_verbose_name()), 180 'verbose_name_plural': force_unicode(opts.get_verbose_name(0)) 180 181 } 181 182 182 183 … … 194 195 if n is None: 195 196 n = obj.count() 196 197 obj = obj.model 197 d = model_format_dict(obj) 198 singular, plural = d["verbose_name"], d["verbose_name_plural"] 199 return ungettext(singular, plural, n or 0) 198 else: 199 if n is None: 200 n = 0 201 singular = obj._meta.get_verbose_name(1) 202 plural = obj._meta.get_verbose_name(n) 203 return ungettext(singular, plural, n) 200 204 201 205 202 206 def lookup_field(name, obj, model_admin=None): … … 229 233 def label_for_field(name, model, model_admin=None, return_attr=False): 230 234 """ 231 235 Returns a sensible label for a field name. The name can be a callable or the 232 name of an object attribute s, as well as a genuine fields. If return_attr is236 name of an object attribute, as well as a genuine field. If return_attr is 233 237 True, the resolved attribute (which could be a callable) is also returned. 234 238 This will be None if (and only if) the name refers to a field. 235 239 """ … … 237 241 try: 238 242 field = model._meta.get_field_by_name(name)[0] 239 243 if isinstance(field, RelatedObject): 240 label = field.opts. verbose_name244 label = field.opts.get_verbose_name() 241 245 else: 242 246 label = field.verbose_name 243 247 except models.FieldDoesNotExist: 244 248 if name == "__unicode__": 245 label = force_unicode(model._meta. verbose_name)249 label = force_unicode(model._meta.get_verbose_name()) 246 250 attr = unicode 247 251 elif name == "__str__": 248 label = smart_str(model._meta. verbose_name)252 label = smart_str(model._meta.get_verbose_name()) 249 253 attr = str 250 254 else: 251 255 if callable(name): … … 311 315 312 316 313 317 def get_model_from_relation(field): 314 if isinstance(field, models.related.RelatedObject):318 if isinstance(field, RelatedObject): 315 319 return field.model 316 320 elif getattr(field, 'rel'): # or isinstance? 317 321 return field.rel.to -
django/contrib/admin/views/main.py
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
a b 81 81 title = ugettext('Select %s') 82 82 else: 83 83 title = ugettext('Select %s to change') 84 self.title = title % force_unicode(self.opts. verbose_name)84 self.title = title % force_unicode(self.opts.get_verbose_name()) 85 85 self.pk_attname = self.lookup_opts.pk.attname 86 86 87 87 def get_filters(self, request, use_distinct=False): -
django/contrib/auth/models.py
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
a b 5 5 from django.db import models 6 6 from django.db.models.manager import EmptyManager 7 7 from django.utils.encoding import smart_str 8 from django.utils.translation import ugettext_lazy as _9 8 from django.utils import timezone 9 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 10 10 11 11 from django.contrib import auth 12 12 from django.contrib.auth.signals import user_logged_in … … 54 54 objects = PermissionManager() 55 55 56 56 class Meta: 57 verbose_name = _('permission')58 verbose_name_plural = _('permissions')59 57 unique_together = (('content_type', 'codename'),) 60 58 ordering = ('content_type__app_label', 'content_type__model', 'codename') 61 59 … … 69 67 return (self.codename,) + self.content_type.natural_key() 70 68 natural_key.dependencies = ['contenttypes.contenttype'] 71 69 70 @classmethod 71 def verbose_names(cls, count=1): 72 return ungettext_lazy('permission', 'permissions', count) 73 72 74 class Group(models.Model): 73 75 """Groups are a generic way of categorizing users to apply permissions, or some other label, to those users. A user can belong to any number of groups. 74 76 … … 79 81 name = models.CharField(_('name'), max_length=80, unique=True) 80 82 permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True) 81 83 82 class Meta:83 verbose_name = _('group')84 verbose_name_plural = _('groups')85 86 84 def __unicode__(self): 87 85 return self.name 88 86 87 @classmethod 88 def verbose_names(cls, count=1): 89 return ungettext_lazy('group', 'groups', count) 90 89 91 class UserManager(models.Manager): 90 92 def create_user(self, username, email=None, password=None): 91 93 """ … … 188 190 user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True) 189 191 objects = UserManager() 190 192 191 class Meta:192 verbose_name = _('user')193 verbose_name_plural = _('users')194 195 193 def __unicode__(self): 196 194 return self.username 197 195 … … 337 335 raise SiteProfileNotAvailable 338 336 return self._profile_cache 339 337 338 @classmethod 339 def verbose_names(cls, count=1): 340 return ungettext_lazy('user', 'users', count) 341 340 342 341 343 class AnonymousUser(object): 342 344 id = None -
django/contrib/comments/models.py
diff --git a/django/contrib/comments/models.py b/django/contrib/comments/models.py
a b 5 5 from django.contrib.sites.models import Site 6 6 from django.db import models 7 7 from django.core import urlresolvers 8 from django.utils.translation import ugettext_lazy as _9 8 from django.utils import timezone 9 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 10 10 from django.conf import settings 11 11 12 12 COMMENT_MAX_LENGTH = getattr(settings,'COMMENT_MAX_LENGTH',3000) … … 73 73 db_table = "django_comments" 74 74 ordering = ('submit_date',) 75 75 permissions = [("can_moderate", "Can moderate comments")] 76 verbose_name = _('comment')77 verbose_name_plural = _('comments')78 76 79 77 def __unicode__(self): 80 78 return "%s: %s..." % (self.name, self.comment[:50]) … … 152 150 } 153 151 return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d 154 152 153 @classmethod 154 def verbose_names(cls, count=1): 155 return ungettext_lazy('comment', 'comments', count) 156 155 157 class CommentFlag(models.Model): 156 158 """ 157 159 Records a flag on a comment. This is intentionally flexible; right now, a … … 178 180 class Meta: 179 181 db_table = 'django_comment_flags' 180 182 unique_together = [('user', 'comment', 'flag')] 181 verbose_name = _('comment flag')182 verbose_name_plural = _('comment flags')183 183 184 184 def __unicode__(self): 185 185 return "%s flag of comment ID %s by %s" % \ … … 189 189 if self.flag_date is None: 190 190 self.flag_date = timezone.now() 191 191 super(CommentFlag, self).save(*args, **kwargs) 192 193 @classmethod 194 def verbose_names(cls, count=1): 195 return ungettext_lazy('comment flag', 'comment flags', count) -
django/contrib/contenttypes/models.py
diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py
a b 1 1 from django.db import models 2 from django.utils.translation import ugettext_lazy as _ 2 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 3 3 from django.utils.encoding import smart_unicode, force_unicode 4 4 5 5 class ContentTypeManager(models.Manager): … … 127 127 objects = ContentTypeManager() 128 128 129 129 class Meta: 130 verbose_name = _('content type')131 verbose_name_plural = _('content types')132 130 db_table = 'django_content_type' 133 131 ordering = ('name',) 134 132 unique_together = (('app_label', 'model'),) … … 145 143 if not model or self.name != model._meta.verbose_name_raw: 146 144 return self.name 147 145 else: 148 return force_unicode(model._meta. verbose_name)146 return force_unicode(model._meta.get_verbose_name()) 149 147 150 148 def model_class(self): 151 149 "Returns the Python model class for this type of content." … … 170 168 171 169 def natural_key(self): 172 170 return (self.app_label, self.model) 171 172 @classmethod 173 def verbose_names(cls, count=1): 174 return ungettext_lazy('content type', 'content types', count) -
django/contrib/databrowse/datastructures.py
diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py
a b 18 18 self.site = site 19 19 self.model = model 20 20 self.model_list = site.registry.keys() 21 self.verbose_name = model._meta. verbose_name22 self.verbose_name_plural = model._meta. verbose_name_plural21 self.verbose_name = model._meta.get_verbose_name() 22 self.verbose_name_plural = model._meta.get_verbose_name(0) 23 23 24 24 def __repr__(self): 25 25 return '<EasyModel for %s>' % smart_str(self.model._meta.object_name) -
django/contrib/flatpages/models.py
diff --git a/django/contrib/flatpages/models.py b/django/contrib/flatpages/models.py
a b 1 1 from django.db import models 2 2 from django.contrib.sites.models import Site 3 from django.utils.translation import ugettext_lazy as _ 3 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 4 4 5 5 6 6 class FlatPage(models.Model): … … 15 15 16 16 class Meta: 17 17 db_table = 'django_flatpage' 18 verbose_name = _('flat page')19 verbose_name_plural = _('flat pages')20 18 ordering = ('url',) 21 19 22 20 def __unicode__(self): … … 24 22 25 23 def get_absolute_url(self): 26 24 return self.url 25 26 @classmethod 27 def verbose_names(cls, count=1): 28 return ungettext_lazy('flat page', 'flat pages', count) -
django/contrib/redirects/models.py
diff --git a/django/contrib/redirects/models.py b/django/contrib/redirects/models.py
a b 1 1 from django.db import models 2 2 from django.contrib.sites.models import Site 3 from django.utils.translation import ugettext_lazy as _ 3 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 4 4 5 5 class Redirect(models.Model): 6 6 site = models.ForeignKey(Site) … … 10 10 help_text=_("This can be either an absolute path (as above) or a full URL starting with 'http://'.")) 11 11 12 12 class Meta: 13 verbose_name = _('redirect')14 verbose_name_plural = _('redirects')15 13 db_table = 'django_redirect' 16 14 unique_together=(('site', 'old_path'),) 17 15 ordering = ('old_path',) 18 16 19 17 def __unicode__(self): 20 18 return "%s ---> %s" % (self.old_path, self.new_path) 19 20 @classmethod 21 def verbose_names(cls, count=1): 22 return ungettext_lazy('redirect', 'redirects', count) -
django/contrib/sessions/models.py
diff --git a/django/contrib/sessions/models.py b/django/contrib/sessions/models.py
a b 1 1 from django.db import models 2 from django.utils.translation import ugettext_lazy as _ 2 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 3 3 4 4 5 5 class SessionManager(models.Manager): … … 43 43 44 44 class Meta: 45 45 db_table = 'django_session' 46 verbose_name = _('session')47 verbose_name_plural = _('sessions')48 46 49 47 def get_decoded(self): 50 48 return SessionStore().decode(self.session_data) 51 49 50 @classmethod 51 def verbose_names(cls, count=1): 52 return ungettext_lazy('session', 'sessions', count) 53 52 54 53 55 # At bottom to avoid circular import 54 56 from django.contrib.sessions.backends.db import SessionStore -
django/contrib/sites/models.py
diff --git a/django/contrib/sites/models.py b/django/contrib/sites/models.py
a b 1 1 from django.db import models 2 from django.utils.translation import ugettext_lazy as _ 2 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 3 3 4 4 5 5 SITE_CACHE = {} … … 40 40 41 41 class Meta: 42 42 db_table = 'django_site' 43 verbose_name = _('site')44 verbose_name_plural = _('sites')45 43 ordering = ('domain',) 46 44 47 45 def __unicode__(self): … … 61 59 except KeyError: 62 60 pass 63 61 62 @classmethod 63 def verbose_names(cls, count=1): 64 return ungettext_lazy('site', 'sites', count) 65 64 66 65 67 class RequestSite(object): 66 68 """ -
django/db/models/base.py
diff --git a/django/db/models/base.py b/django/db/models/base.py
a b 98 98 for obj_name, obj in attrs.items(): 99 99 new_class.add_to_class(obj_name, obj) 100 100 101 if hasattr(new_class, 'verbose_names'): 102 new_class._meta._verbose_name = new_class._meta.get_verbose_name() 103 new_class._meta._verbose_name_plural = new_class._meta.get_verbose_name(0) 104 101 105 # All the fields of any type declared on this model 102 106 new_fields = new_class._meta.local_fields + \ 103 107 new_class._meta.local_many_to_many + \ … … 770 774 771 775 def unique_error_message(self, model_class, unique_check): 772 776 opts = model_class._meta 773 model_name = capfirst(opts. verbose_name)777 model_name = capfirst(opts.get_verbose_name()) 774 778 775 779 # A unique field 776 780 if len(unique_check) == 1: -
django/db/models/fields/related.py
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
a b 115 115 def set_attributes_from_rel(self): 116 116 self.name = self.name or (self.rel.to._meta.object_name.lower() + '_' + self.rel.to._meta.pk.name) 117 117 if self.verbose_name is None: 118 self.verbose_name = self.rel.to._meta. verbose_name118 self.verbose_name = self.rel.to._meta.get_verbose_name() 119 119 self.rel.field_name = self.rel.field_name or self.rel.to._meta.pk.name 120 120 121 121 def do_related_class(self, other, cls): … … 940 940 qs = qs.complex_filter(self.rel.limit_choices_to) 941 941 if not qs.exists(): 942 942 raise exceptions.ValidationError(self.error_messages['invalid'] % { 943 'model': self.rel.to._meta. verbose_name, 'pk': value})943 'model': self.rel.to._meta.get_verbose_name(), 'pk': value}) 944 944 945 945 def get_attname(self): 946 946 return '%s_id' % self.name … … 1052 1052 def create_many_to_many_intermediary_model(field, klass): 1053 1053 from django.db import models 1054 1054 managed = True 1055 1056 def verbose_name_method(f, t): 1057 def verbose_names(cls, count=1): 1058 return '%(from)s-%(to)s relationship%(extra)s' % {'from': f, 'to': t, 'extra': 's' if count != 1 else ''} 1059 return verbose_names 1060 1055 1061 if isinstance(field.rel.to, basestring) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT: 1056 1062 to_model = field.rel.to 1057 1063 to = to_model.split('.')[-1] … … 1080 1086 'app_label': klass._meta.app_label, 1081 1087 'db_tablespace': klass._meta.db_tablespace, 1082 1088 'unique_together': (from_, to), 1083 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to},1084 'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to},1085 1089 }) 1086 1090 # Construct and return the new class. 1087 1091 return type(name, (models.Model,), { 1088 1092 'Meta': meta, 1089 1093 '__module__': klass.__module__, 1090 1094 from_: models.ForeignKey(klass, related_name='%s+' % name, db_tablespace=field.db_tablespace), 1091 to: models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace) 1095 to: models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace), 1096 'verbose_names': classmethod(verbose_name_method(from_, to)), 1092 1097 }) 1093 1098 1094 1099 class ManyToManyField(RelatedField, Field): -
django/db/models/options.py
diff --git a/django/db/models/options.py b/django/db/models/options.py
a b 1 from bisect import bisect 1 2 import re 2 from bisect import bisect 3 import warnings 3 4 4 5 from django.conf import settings 5 from django.db.models.related import RelatedObject6 6 from django.db.models.fields.related import ManyToManyRel 7 7 from django.db.models.fields import AutoField, FieldDoesNotExist 8 8 from django.db.models.fields.proxy import OrderWrt 9 9 from django.db.models.loading import get_models, app_cache_ready 10 from django.db.models.related import RelatedObject 11 from django.utils.datastructures import SortedDict 12 from django.utils.encoding import force_unicode, smart_str 13 from django.utils.functional import cached_property 10 14 from django.utils.translation import activate, deactivate_all, get_language, string_concat 11 from django.utils.encoding import force_unicode, smart_str12 from django.utils.datastructures import SortedDict13 15 14 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces". 15 get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip() 16 17 DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 16 DEFAULT_NAMES = ('db_table', 'ordering', 18 17 'unique_together', 'permissions', 'get_latest_by', 19 18 'order_with_respect_to', 'app_label', 'db_tablespace', 20 19 'abstract', 'managed', 'proxy', 'auto_created') 21 20 21 DEPRECATED_NAMES = ('verbose_name', 'verbose_name_plural') 22 23 CAMEL_CASE_RE = re.compile('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))') 24 22 25 class Options(object): 23 26 def __init__(self, meta, app_label=None): 24 27 self.local_fields, self.local_many_to_many = [], [] 25 28 self.virtual_fields = [] 26 self.module_name, self.verbose_name = None, None 27 self.verbose_name_plural = None 29 self.cls = None 30 self.module_name, self._verbose_name = None, None 31 self._verbose_name_plural = None 28 32 self.db_table = '' 29 33 self.ordering = [] 30 34 self.unique_together = [] … … 54 58 # from *other* models. Needed for some admin checks. Internal use only. 55 59 self.related_fkey_lookups = [] 56 60 61 def _get_verbose_name(self): 62 warnings.warn("Meta.verbose_name is deprecated. Use a verbose_names()" 63 " classmethod in the model instead.", PendingDeprecationWarning) 64 return self._verbose_name 65 def _set_verbose_name(self, value): 66 self._verbose_name = value 67 verbose_name = property(_get_verbose_name, _set_verbose_name) 68 69 def _get_verbose_name_plural(self): 70 warnings.warn("Meta.verbose_name_plural is deprecated. Use a " 71 "verbose_names() classmethod in the model instead.", 72 PendingDeprecationWarning) 73 return self._verbose_name_plural 74 def _set_verbose_name_plural(self, value): 75 self._verbose_name_plural = value 76 verbose_name_plural = property(_get_verbose_name_plural, _set_verbose_name_plural) 77 57 78 def contribute_to_class(self, cls, name): 58 79 from django.db import connection 59 80 from django.db.backends.util import truncate_name 60 81 82 self.cls = cls 61 83 cls._meta = self 62 84 self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS 63 85 # First, construct the default values for these options. 64 86 self.object_name = cls.__name__ 65 87 self.module_name = self.object_name.lower() 66 self. verbose_name = get_verbose_name(self.object_name)88 self._verbose_name = CAMEL_CASE_RE.sub(' \\1', self.object_name).lower().strip() 67 89 68 90 # Next, apply any overridden values from 'class Meta'. 69 91 if self.meta: … … 80 102 elif hasattr(self.meta, attr_name): 81 103 setattr(self, attr_name, getattr(self.meta, attr_name)) 82 104 105 for attr_name in DEPRECATED_NAMES: 106 if attr_name in meta_attrs: 107 warnings.warn("%(cls)s: Meta.%(attr_name)s is deprecated. Use a " 108 "verbose_names() classmethod in the model " 109 "instead." % {'cls': cls, 'attr_name': attr_name}, 110 PendingDeprecationWarning) 111 setattr(self, '_%s' % attr_name, meta_attrs.pop(attr_name)) 112 83 113 # unique_together can be either a tuple of tuples, or a single 84 114 # tuple of two strings. Normalize it to a tuple of tuples, so that 85 115 # calling code can uniformly expect that. … … 90 120 91 121 # verbose_name_plural is a special case because it uses a 's' 92 122 # by default. 93 if self. verbose_name_plural is None:94 self. verbose_name_plural = string_concat(self.verbose_name, 's')123 if self._verbose_name_plural is None: 124 self._verbose_name_plural = string_concat(self._verbose_name, 's') 95 125 96 126 # Any leftover attributes must be invalid. 97 127 if meta_attrs != {}: 98 128 raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())) 99 129 else: 100 self. verbose_name_plural = string_concat(self.verbose_name, 's')130 self._verbose_name_plural = string_concat(self._verbose_name, 's') 101 131 del self.meta 102 132 103 133 # If the db_table wasn't provided, use the app_label + module_name. … … 199 229 """ 200 230 lang = get_language() 201 231 deactivate_all() 202 raw = force_unicode(self. verbose_name)232 raw = force_unicode(self._verbose_name) 203 233 activate(lang) 204 234 return raw 205 verbose_name_raw = property(verbose_name_raw)235 verbose_name_raw = cached_property(verbose_name_raw) 206 236 207 237 def _fields(self): 208 238 """ … … 495 525 Returns the index of the primary key field in the self.fields list. 496 526 """ 497 527 return self.fields.index(self.pk) 528 529 def get_verbose_name(self, count=1): 530 if hasattr(self.cls, 'verbose_names'): 531 retv = self.cls.verbose_names(count) 532 if retv is not None: 533 return retv 534 if count == 1: 535 return CAMEL_CASE_RE.sub(' \\1', self.object_name).lower().strip() 536 return string_concat(self.get_verbose_name(1), 's') -
django/db/models/related.py
diff --git a/django/db/models/related.py b/django/db/models/related.py
a b 36 36 {'%s__isnull' % self.parent_model._meta.module_name: False}) 37 37 lst = [(x._get_pk_val(), smart_unicode(x)) for x in queryset] 38 38 return first_choice + lst 39 39 40 40 def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False): 41 41 # Defer to the actual field definition for db prep 42 42 return self.field.get_db_prep_lookup(lookup_type, value, … … 67 67 68 68 def get_cache_name(self): 69 69 return "_%s_cache" % self.get_accessor_name() 70 71 def get_verbose_name(self, count=1): 72 return self.model._meta.get_verbose_name(count) -
django/utils/functional.py
diff --git a/django/utils/functional.py b/django/utils/functional.py
a b 31 31 32 32 class cached_property(object): 33 33 """ 34 Decorator that c reates converts a method with a single35 self argument into aproperty cached on the instance.34 Decorator that converts a method with a single self argument into a 35 property cached on the instance. 36 36 """ 37 37 def __init__(self, func): 38 38 self.func = func -
django/views/generic/create_update.py
diff --git a/django/views/generic/create_update.py b/django/views/generic/create_update.py
a b 95 95 return model.objects.get(**lookup_kwargs) 96 96 except ObjectDoesNotExist: 97 97 raise Http404("No %s found for %s" 98 % (model._meta. verbose_name, lookup_kwargs))98 % (model._meta.get_verbose_name(), lookup_kwargs)) 99 99 100 100 def create_object(request, model=None, template_name=None, 101 101 template_loader=loader, extra_context=None, post_save_redirect=None, … … 119 119 new_object = form.save() 120 120 121 121 msg = ugettext("The %(verbose_name)s was created successfully.") %\ 122 {"verbose_name": model._meta. verbose_name}122 {"verbose_name": model._meta.get_verbose_name()} 123 123 messages.success(request, msg, fail_silently=True) 124 124 return redirect(post_save_redirect, new_object) 125 125 else: … … 162 162 if form.is_valid(): 163 163 obj = form.save() 164 164 msg = ugettext("The %(verbose_name)s was updated successfully.") %\ 165 {"verbose_name": model._meta. verbose_name}165 {"verbose_name": model._meta.get_verbose_name()} 166 166 messages.success(request, msg, fail_silently=True) 167 167 return redirect(post_save_redirect, obj) 168 168 else: … … 205 205 if request.method == 'POST': 206 206 obj.delete() 207 207 msg = ugettext("The %(verbose_name)s was deleted.") %\ 208 {"verbose_name": model._meta. verbose_name}208 {"verbose_name": model._meta.get_verbose_name()} 209 209 messages.success(request, msg, fail_silently=True) 210 210 return HttpResponseRedirect(post_delete_redirect) 211 211 else: -
django/views/generic/date_based.py
diff --git a/django/views/generic/date_based.py b/django/views/generic/date_based.py
a b 34 34 queryset = queryset.filter(**{'%s__lte' % date_field: datetime.datetime.now()}) 35 35 date_list = queryset.dates(date_field, 'year')[::-1] 36 36 if not date_list and not allow_empty: 37 raise Http404("No %s available" % model._meta. verbose_name)37 raise Http404("No %s available" % model._meta.get_verbose_name()) 38 38 39 39 if date_list and num_latest: 40 40 latest = queryset.order_by('-'+date_field)[:num_latest] … … 354 354 try: 355 355 obj = queryset.get(**lookup_kwargs) 356 356 except ObjectDoesNotExist: 357 raise Http404("No %s found for" % model._meta. verbose_name)357 raise Http404("No %s found for" % model._meta.get_verbose_name()) 358 358 if not template_name: 359 359 template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower()) 360 360 if template_name_field: -
django/views/generic/dates.py
diff --git a/django/views/generic/dates.py b/django/views/generic/dates.py
a b 195 195 196 196 if not allow_empty and not qs: 197 197 raise Http404(_(u"No %(verbose_name_plural)s available") % { 198 'verbose_name_plural': force_unicode(qs.model._meta. verbose_name_plural)198 'verbose_name_plural': force_unicode(qs.model._meta.get_verbose_name(0)) 199 199 }) 200 200 201 201 return qs … … 464 464 465 465 if not self.get_allow_future() and date > datetime.date.today(): 466 466 raise Http404(_(u"Future %(verbose_name_plural)s not available because %(class_name)s.allow_future is False.") % { 467 'verbose_name_plural': qs.model._meta. verbose_name_plural,467 'verbose_name_plural': qs.model._meta.get_verbose_name(0), 468 468 'class_name': self.__class__.__name__, 469 469 }) 470 470 -
django/views/generic/detail.py
diff --git a/django/views/generic/detail.py b/django/views/generic/detail.py
a b 49 49 obj = queryset.get() 50 50 except ObjectDoesNotExist: 51 51 raise Http404(_(u"No %(verbose_name)s found matching the query") % 52 {'verbose_name': queryset.model._meta. verbose_name})52 {'verbose_name': queryset.model._meta.get_verbose_name()}) 53 53 return obj 54 54 55 55 def get_queryset(self): -
django/views/generic/list_detail.py
diff --git a/django/views/generic/list_detail.py b/django/views/generic/list_detail.py
a b 131 131 try: 132 132 obj = queryset.get() 133 133 except ObjectDoesNotExist: 134 raise Http404("No %s found matching the query" % (model._meta. verbose_name))134 raise Http404("No %s found matching the query" % (model._meta.get_verbose_name())) 135 135 if not template_name: 136 136 template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower()) 137 137 if template_name_field: -
docs/ref/models/instances.txt
diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt
a b 540 540 More details on named URL patterns are in the :doc:`URL dispatch documentation 541 541 </topics/http/urls>`. 542 542 543 ``verbose_names`` 544 ----------------- 545 546 .. classmethod:: Model.verbose_names(count=1) 547 548 .. versionadded:: 1.4 549 550 A Python classmethod. 551 552 This method has no default implementation and you might provide one depending 553 on the the human readable name you need for your model. 554 555 The *count* argument is the quantity of model instances the verbose name is 556 being requested for. 557 558 It provides a straight migration path from the :attr:`~Options.verbose_name` and 559 :attr:`~Options.verbose_name_plural` options that entered a deprecation cycle 560 starting with Django 1.4. For example this model declaration:: 561 562 class SSN(models.Model): 563 value = models.CharField(max_length=11) 564 565 class Meta: 566 verbose_name = 'social security number' 567 568 needs to be changed to:: 569 570 class SSN(models.Model): 571 value = models.CharField(max_length=11) 572 573 @classmethod 574 def verbose_names(cls, count=1): 575 return 'social security number' 576 577 and this one:: 578 579 class SecurityPolicy(models.Model): 580 title = models.CharField(max_length=30) 581 582 class Meta: 583 verbose_name_plural = 'security policies' 584 585 should be changed to:: 586 587 class SecurityPolicy(models.Model): 588 title = models.CharField(max_length=30) 589 590 @classmethod 591 def verbose_names(cls, count=1): 592 if count != 1: 593 return 'security policies' 594 595 This new syntax can take in account the number of model instances at play to 596 decide the exact verbose name to show in user interaction contexts. It provides 597 for better internationalization of your application because the name of your 598 model is now translatable in a more correct way to many more locales:: 599 600 from django.utils.translation import ugettext_lazy 601 602 class Man(models.Model): 603 first_name = models.CharField(max_length=30) 604 605 @classmethod 606 def verbose_names(cls, count=1): 607 if count == 1: 608 return ugettext_lazy('man') 609 else: 610 return ugettext_lazy('men') 611 612 Although you usually will use the 613 :func:`~django.utils.translation.ungettext_lazy` function:: 614 615 from django.utils.translation import ungettext_lazy 616 617 class Library(models.Model): 618 city_name = models.CharField(max_length=30) 619 620 @classmethod 621 def verbose_names(cls, count=1): 622 return ungetttext_lazy('llbrary', 'libraries', count) 623 624 .. note:: 625 Remember to declare this method as a classmethod:: 626 627 class MyModel(models.Model): 628 ... 629 630 @classmethod 631 def verbose_names(cls, count=1): 632 ... 633 543 634 Extra instance methods 544 635 ====================== 545 636 546 637 In addition to :meth:`~Model.save()`, :meth:`~Model.delete()`, a model object 547 638 might have some of the following methods: 548 639 640 ``get_*_display`` 641 ----------------- 642 549 643 .. method:: Model.get_FOO_display() 550 644 551 645 For every field that has :attr:`~django.db.models.Field.choices` set, the … … 570 664 >>> p.get_gender_display() 571 665 'Male' 572 666 667 ``get_next_by_*`` and ``get_prev_by_*`` 668 --------------------------------------- 669 573 670 .. method:: Model.get_next_by_FOO(\**kwargs) 574 671 .. method:: Model.get_previous_by_FOO(\**kwargs) 575 672 … … 586 683 Note that in the case of identical date values, these methods will use the 587 684 primary key as a tie-breaker. This guarantees that no records are skipped or 588 685 duplicated. That also means you cannot use those methods on unsaved objects. 589 -
docs/ref/models/options.txt
diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt
a b 253 253 254 254 .. attribute:: Options.verbose_name 255 255 256 .. deprecated:: 1.4 257 This option has been replaced by the :meth:`~Model.verbose_names` model 258 classmethod. Implement such method in your model to make Django aware 259 of its human readable name(s). 260 256 261 A human-readable name for the object, singular:: 257 262 258 263 verbose_name = "pizza" … … 265 270 266 271 .. attribute:: Options.verbose_name_plural 267 272 273 .. deprecated:: 1.4 274 This option has been replaced by the :meth:`~Model.verbose_names` model 275 classmethod. Implement such method in your model to make Django aware 276 of its human readable name(s). 277 268 278 The plural name for the object:: 269 279 270 280 verbose_name_plural = "stories" 271 281 272 282 If this isn't given, Django will use :attr:`~Options.verbose_name` + ``"s"``. 283 284 ``Meta`` methods 285 ================ 286 287 ``get_verbose_name`` 288 -------------------- 289 290 .. method:: Options.get_verbose_name(count=1) 291 292 .. versionadded:: 1.4 293 294 It provides an API to access translated and correctly pluralized verbose names 295 of models (something that previously involved accessing the 296 ``Model._meta.verbose_name`` and ``Model._meta.verbose_name_plural`` 297 attributes.) 298 299 .. seealso:: 300 301 The :meth:`~Model.verbose_names` user-provided classmethod that works 302 together with this method. 303 304 This method will always return a value independently of whether the model 305 implements the :meth:`~Model.verbose_names` classmethod or not. Django provides 306 fallback return values compatible with the default values of the deprecated 307 :attr:`~Options.verbose_name` and :attr:`~Options.verbose_name_plural` options. 308 309 For example, given this model:: 310 311 class Door(models.Model): 312 height = models.PositiveIntegerField() 313 314 then these are the return values of this method:: 315 316 >>> Door._meta.get_verbose_name(1) # One door 317 >>> 'door' # Automatically provided singular verbose name 318 >>> Door._meta.get_verbose_name(3) # More than one door 319 >>> 'doors' 320 # Note how it returns an automatically provided simple naive pluralization 321 # appending a 's' to the singular value 322 323 Or, for the examples in the :meth:`~Model.verbose_names` documentation:: 324 325 >>> SSN._meta.get_verbose_name() # One SSN, count default value 326 >>> 'social security number' 327 # Note how it returns the value returned by SSN.verbose_names(count) for a 328 # value of count=1 329 >>> SSN._meta.get_verbose_name(0) # Zero SSN 330 >>> 'social security numbers' 331 # Note how it returns an automatically provided simple naive pluralization 332 # appending a 's' to the singular value 333 334 >>> SecurityPolicy._meta.get_verbose_name() # One policy 335 >>> 'security policy' 336 # Note how it returns a value automatically provided by Django by processing 337 # the model class name 338 >>> SecurityPolicy._meta.get_verbose_name(10) # Ten policies 339 >>> 'security policies' 340 # Note how it returns the value returned by 341 # SecurityPolicy.verbose_names(count) for a count value different from 1 -
docs/topics/db/models.txt
diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt
a b 641 641 642 642 class Meta: 643 643 ordering = ["horn_length"] 644 verbose_name_plural = "oxen"645 644 646 645 Model metadata is "anything that's not a field", such as ordering options 647 (:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`), or 648 human-readable singular and plural names (:attr:`~Options.verbose_name` and 649 :attr:`~Options.verbose_name_plural`). None are required, and adding ``class 650 Meta`` to a model is completely optional. 646 (:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`). 647 None are required, and adding ``class Meta`` to a model is completely optional. 651 648 652 649 A complete list of all possible ``Meta`` options can be found in the :doc:`model 653 650 option reference </ref/models/options>`. -
docs/topics/i18n/translation.txt
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
a b 168 168 translation string and the number of objects. 169 169 170 170 This function is useful when you need your Django application to be localizable 171 to languages where the number and complexity of `plural forms 172 <http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms>`_ is 173 greater than the two forms used in English ('object' for the singular and 174 'objects' for all the cases where ``count`` is different from one, irrespective 175 of its value.) 171 to languages where the number and complexity of `plural forms`_ is greater than 172 the two forms as used in the English language (e.g. 'object' for the singular 173 and 'objects' for all the cases where ``count`` is different from one, 174 irrespective of its value.) 176 175 177 176 For example:: 178 177 … … 187 186 } 188 187 return HttpResponse(page) 189 188 190 In this example the number of objects is passed to the translation 191 languages asthe ``count`` variable.189 In this example the number of objects is passed to the translation functions as 190 the ``count`` variable. 192 191 193 Lets see a slightly more complex usageexample::192 Lets see a slightly more complex example:: 194 193 195 194 from django.utils.translation import ungettext 196 195 197 196 count = Report.objects.count() 198 if count == 1: 199 name = Report._meta.verbose_name 200 else: 201 name = Report._meta.verbose_name_plural 197 name = Report._meta.get_verbose_name(count) 202 198 203 199 text = ungettext( 204 200 'There is %(count)d %(name)s available.', … … 209 205 'name': name 210 206 } 211 207 212 Here we reuse localizable, hopefully already translated literals (contained in 213 the ``verbose_name`` and ``verbose_name_plural`` model ``Meta`` options) for 214 other parts of the sentence so all of it is consistently based on the 215 cardinality of the elements at play. 208 Here we reuse localizable, potentially translated literals (as returned by the 209 :meth:`django.db.models.Options.get_verbose_name` method) for other parts of the 210 sentence so all of it is consistently based on the cardinality of the elements 211 at play. 212 213 .. _plural forms: http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms 216 214 217 215 .. _pluralization-var-notes: 218 216 … … 294 292 Lazy translation 295 293 ---------------- 296 294 297 Use the function :func:`django.utils.translation.ugettext_lazy()` to translate 298 strings lazily -- when the value is accessed rather than when the 299 ``ugettext_lazy()`` function is called. 295 Use the lazy versions of translation functions in 296 :mod:`django.utils.translation` (easily recognizable by the ``lazy`` suffix in 297 their names) to translate strings lazily -- when the value is accessed rather 298 than when they are called. 300 299 301 For example, to translate a model's ``help_text``, do the following:: 300 This is an essential need when we need to mark for translation text that is 301 intermixed with code that is executed at module load time. 302 303 As this is something that can easily happen when defining Django models (the 304 declarative notation is implemented in a way such that model fields are actually 305 class level attributes) this means you need to make sure to use lazy 306 translations in the following cases: 307 308 Model fields and relationship ``verbose_name`` and ``help_text`` option values 309 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 310 311 For example, to translate the help text of the *name* field in the following 312 model, do the following:: 302 313 303 314 from django.utils.translation import ugettext_lazy 304 315 … … 310 321 is used in a string context, such as template rendering on the Django admin 311 322 site. 312 323 324 In addition to normal fields your models might also contain relationships: 325 ``ForeignKey``, ``ManyTomanyField`` or ``OneToOneField`` fields. You can mark 326 the name of a relationship itself as translatable by using its ``verbose_name`` 327 option:: 328 329 from django.utils.translation import ugettext_lazy as _ 330 331 class MyThing(models.Model): 332 kind = models.ForeignKey(ThingKind, related_name='kinds', 333 verbose_name=_('kind')) 334 335 Just like you would do in the :meth:`~django.db.models.Model.verbose_names` 336 model classmethod, you should provide a lowercase verbose name text for the 337 relation as Django will automatically titlecase it when required. 338 339 Model verbose names values 340 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 341 342 It is recommended to always provide a 343 :meth:`~django.db.models.Model.verbose_names` method rather than relying on 344 Django's default English-centric and somewhat naïve determination of verbose 345 names:: 346 347 from django.utils.translation import ugettext_lazy 348 349 class MyThing(models.Model): 350 name = models.CharField(_('name'), help_text=ugettext_lazy('This is the help text')) 351 352 @classmethod 353 def verbose_names(cls, count=1): 354 if count == 1: 355 return ugettext_lazy('my thing') 356 else: 357 return ugettext_lazy('my things') 358 359 In this particular case it is almost always a better idea to actually use 360 :func:`~django.utils.translation.ungettext_lazy` instead of ``ugettext_lazy``. 361 Refer to the ``verbose_names()`` documentation linked above for details on how 362 this can make the lifes of your translators and users easier. 363 364 Model methods ``short_description`` attribute values 365 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 366 367 For model methods, you can provide translations to Django and the admin site 368 with the ``short_description`` parameter set on the corresponding method:: 369 370 from django.utils.translation import ugettext_lazy as _ 371 372 class MyThing(models.Model): 373 kind = models.ForeignKey(ThingKind, related_name='kinds', 374 verbose_name=_('kind')) 375 376 def is_mouse(self): 377 return self.kind.type == MOUSE_TYPE 378 is_mouse.short_description = _('Is it a mouse?') 379 380 Notes on translation in models 381 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 382 313 383 The result of a ``ugettext_lazy()`` call can be used wherever you would use a 314 384 unicode string (an object with type ``unicode``) in Python. If you try to use 315 385 it where a bytestring (a ``str`` object) is expected, things will not work as … … 328 398 <django.utils.functional...>"``, you have tried to insert the result of 329 399 ``ugettext_lazy()`` into a bytestring. That's a bug in your code. 330 400 331 If you don't like the verbose name ``ugettext_lazy``, you can just alias it as401 If you don't like the long ``ugettext_lazy`` name, you can just alias it as 332 402 ``_`` (underscore), like so:: 333 403 334 404 from django.utils.translation import ugettext_lazy as _ … … 336 406 class MyThing(models.Model): 337 407 name = models.CharField(help_text=_('This is the help text')) 338 408 339 Always use lazy translations in :doc:`Django models </topics/db/models>`.340 Field names and table names should be marked for translation (otherwise, they341 won't be translated in the admin interface). This means writing explicit342 ``verbose_name`` and ``verbose_name_plural`` options in the ``Meta`` class,343 though, rather than relying on Django's default determination of344 ``verbose_name`` and ``verbose_name_plural`` by looking at the model's class345 name::346 347 from django.utils.translation import ugettext_lazy as _348 349 class MyThing(models.Model):350 name = models.CharField(_('name'), help_text=_('This is the help text'))351 352 class Meta:353 verbose_name = _('my thing')354 verbose_name_plural = _('my things')355 356 Notes on model classes translation357 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~358 359 Your model classes may not only contain normal fields: you may have relations360 (with a ``ForeignKey`` field) or additional model methods you may use for361 columns in the Django admin site.362 363 If you have models with foreign keys and you use the Django admin site, you can364 provide translations for the relation itself by using the ``verbose_name``365 parameter on the ``ForeignKey`` object::366 367 class MyThing(models.Model):368 kind = models.ForeignKey(ThingKind, related_name='kinds',369 verbose_name=_('kind'))370 371 As you would do for the ``verbose_name`` and ``verbose_name_plural`` settings of372 a model Meta class, you should provide a lowercase verbose name text for the373 relation as Django will automatically titlecase it when required.374 375 For model methods, you can provide translations to Django and the admin site376 with the ``short_description`` parameter set on the corresponding method::377 378 class MyThing(models.Model):379 kind = models.ForeignKey(ThingKind, related_name='kinds',380 verbose_name=_('kind'))381 382 def is_mouse(self):383 return self.kind.type == MOUSE_TYPE384 is_mouse.short_description = _('Is it a mouse?')385 386 As always with model classes translations, don't forget to use the lazy387 translation method!388 389 409 Working with lazy translation objects 390 410 ------------------------------------- 391 411 -
tests/modeltests/custom_pk/models.py
diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py
a b 26 26 class Business(models.Model): 27 27 name = models.CharField(max_length=20, primary_key=True) 28 28 employees = models.ManyToManyField(Employee) 29 class Meta:30 verbose_name_plural = 'businesses'31 29 32 30 def __unicode__(self): 33 31 return self.name 34 32 33 @classmethod 34 def verbose_names(cls, count=1): 35 return 'businesses' 36 35 37 class Bar(models.Model): 36 38 id = MyAutoField(primary_key=True, db_index=True) 37 39 -
tests/regressiontests/admin_util/models.py
diff --git a/tests/regressiontests/admin_util/models.py b/tests/regressiontests/admin_util/models.py
a b 34 34 event = models.OneToOneField(Event) 35 35 name = models.CharField(max_length=255) 36 36 37 class Meta: 38 verbose_name = "awesome guest" 37 @classmethod 38 def verbose_names(cls, count=1): 39 return "awesome guest" -
tests/regressiontests/admin_views/models.py
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
a b 63 63 def __unicode__(self): 64 64 return self.title 65 65 66 class Meta: 66 @classmethod 67 def verbose_names(cls, count=1): 67 68 # Use a utf-8 bytestring to ensure it works (see #11710) 68 verbose_name ='¿Chapter?'69 return '¿Chapter?' 69 70 70 71 71 72 class ChapterXtra1(models.Model): … … 538 539 age = models.PositiveIntegerField() 539 540 is_employee = models.NullBooleanField() 540 541 541 class PrePopulatedPostLargeSlug(models.Model): 542 """ 543 Regression test for #15938: a large max_length for the slugfield must not 544 be localized in prepopulated_fields_js.html or it might end up breaking 545 the javascript (ie, using THOUSAND_SEPARATOR ends up with maxLength=1,000) 546 """ 547 title = models.CharField(max_length=100) 548 published = models.BooleanField() 542 class PrePopulatedPostLargeSlug(models.Model): 543 """ 544 Regression test for #15938: a large max_length for the slugfield must not 545 be localized in prepopulated_fields_js.html or it might end up breaking 546 the javascript (ie, using THOUSAND_SEPARATOR ends up with maxLength=1,000) 547 """ 548 title = models.CharField(max_length=100) 549 published = models.BooleanField() 549 550 slug = models.SlugField(max_length=1000) 550 551 -
tests/regressiontests/backends/models.py
diff --git a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py
a b 28 28 # Until #13711 is fixed, this test can't be run under MySQL. 29 29 if connection.features.supports_long_model_names: 30 30 class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model): 31 class Meta:32 # We need to use a short actual table name or33 # we hit issue #8548 which we're not testing!34 verbose_name = 'model_with_long_table_name'35 31 primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True) 36 32 charfield_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.CharField(max_length=100) 37 33 m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True) 38 34 35 # We need to use a short actual verbose name or 36 # we hit issue #8548 which we're not testing! 37 @classmethod 38 def verbose_names(cls, count=1): 39 return 'model_with_long_table_name' 40 39 41 40 42 class Tag(models.Model): 41 43 name = models.CharField(max_length=30) -
tests/regressiontests/generic_views/models.py
diff --git a/tests/regressiontests/generic_views/models.py b/tests/regressiontests/generic_views/models.py
a b 1 1 from django.db import models 2 2 from django.utils.translation import ungettext_lazy 3 3 4 4 class Artist(models.Model): 5 5 name = models.CharField(max_length=100) 6 6 7 7 class Meta: 8 8 ordering = ['name'] 9 verbose_name = 'professional artist'10 verbose_name_plural = 'professional artists'11 9 12 10 def __unicode__(self): 13 11 return self.name … … 16 14 def get_absolute_url(self): 17 15 return ('artist_detail', (), {'pk': self.id}) 18 16 17 @classmethod 18 def verbose_names(cls, count=1): 19 return ungettext_lazy('professional artist', 'professional artists', count) 20 19 21 class Author(models.Model): 20 22 name = models.CharField(max_length=100) 21 23 slug = models.SlugField() -
tests/regressiontests/i18n/models.py
diff --git a/tests/regressiontests/i18n/models.py b/tests/regressiontests/i18n/models.py
a b 13 13 cents_payed = models.DecimalField(max_digits=4, decimal_places=2) 14 14 products_delivered = models.IntegerField() 15 15 16 class Meta:17 verbose_name = _('Company')18 No newline at end of file16 @classmethod 17 def verbose_names(cls, count=1): 18 return _('Company') -
tests/regressiontests/model_inheritance_regress/models.py
diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py
a b 108 108 109 109 class Meta: 110 110 abstract = True 111 verbose_name_plural = u'Audits' 111 112 @classmethod 113 def verbose_names(cls, count=1): 114 return u'Audits' 112 115 113 116 class CertificationAudit(AuditBase): 114 117 class Meta(AuditBase.Meta): -
tests/regressiontests/model_regress/models.py
diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py
a b 16 16 17 17 class Meta: 18 18 ordering = ('pub_date','headline') 19 # A utf-8 verbose name (Ångström's Articles) to test they are valid.20 verbose_name = "\xc3\x85ngstr\xc3\xb6m's Articles"21 19 22 20 def __unicode__(self): 23 21 return self.headline 24 22 23 @classmethod 24 def verbose_names(cls, count=1): 25 # An utf-8 verbose name (Ångström's Articles) to test they are valid. 26 return "\xc3\x85ngstr\xc3\xb6m's Articles" 27 25 28 class Movie(models.Model): 26 29 #5218: Test models with non-default primary keys / AutoFields 27 30 movie_id = models.AutoField(primary_key=True) -
new file tests/regressiontests/verbose_names/__init__.py
diff --git a/tests/regressiontests/verbose_names/__init__.py b/tests/regressiontests/verbose_names/__init__.py new file mode 100644 diff --git a/tests/regressiontests/verbose_names/locale/es_AR/LC_MESSAGES/django.mo b/tests/regressiontests/verbose_names/locale/es_AR/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..5a0d607200b2b05b66bf95085b76d736061c03bf GIT binary patch literal 1052 zc$|%q-EPw`7=|-CeiY6)<<E!{KzQUwz~r^4p-rb~NmsS$jyUlWlVwWn%65TX0Nep* zoFQ=^a0EB8b1njjFH5R2Xg939d2Ky!{C%-cZft%uU|a*;0=)uV2YoWC@f~y*^aIoe z{RAP<qm3%RSNjq8ZSeQCe+EAWeXaT9hG8_pgGROA2k@KVUuypbeh2)d_Fv!|bzkMU z1ZvdttDsHLm3n=-e8rM-RZvY23x@jaEmnFCvx!Jqf$rNEaOYgE{v+U?4HPV+$$U-u zv(Q-&|1JC%b&Y`($1Kq}Nbv!aDi@rff!Epdc71f<4@bd%-yE{3kQzrS%TxSvmMI(y zg3Q5wj60H14Ikky)r=s=vTY2H^Bma@fr<6ZvLJ|u>`ks#=bjSF!j=oUkSdKA?Fvay zZybiG??pap`jHoeZLixM`lHbci1KVkGlpYECj^CFe|KEA+zi8Zk#7l^Ei=$+6h!{= z|LiTx><F$IhjQ~N1T=f6pG*sy^Bt5NQK^`2jpH8PUgpZSUNeb(o`^K(8A02Jxi$yI zOwt1Pgq$dX`1EwCTFwrtrmY7Y-D@HHp=m_rp^MYAms}t%X*x^t@>bx2&w)-2)q<)& bu)3JLi_Oc+PuCP)!It}0aBbO@*fo9wrIRVG
-
new file tests/regressiontests/verbose_names/locale/es_AR/LC_MESSAGES/django.po
diff --git a/tests/regressiontests/verbose_names/locale/es_AR/LC_MESSAGES/django.po b/tests/regressiontests/verbose_names/locale/es_AR/LC_MESSAGES/django.po new file mode 100644
- + 1 # SOME DESCRIPTIVE TITLE. 2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 # This file is distributed under the same license as the PACKAGE package. 4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. 5 # 6 msgid "" 7 msgstr "" 8 "Project-Id-Version: PACKAGE VERSION\n" 9 "Report-Msgid-Bugs-To: \n" 10 "POT-Creation-Date: 2011-11-27 12:11-0600\n" 11 "PO-Revision-Date: 2011-11-27 15:00-0300\n" 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" 13 "Language-Team: LANGUAGE <LL@li.org>\n" 14 "Language: \n" 15 "MIME-Version: 1.0\n" 16 "Content-Type: text/plain; charset=UTF-8\n" 17 "Content-Transfer-Encoding: 8bit\n" 18 "Plural-Forms: nplurals=2; plural=(n != 1)\n" 19 20 msgid "Translatable legacy model #1" 21 msgstr "Modelo legado traducible #1" 22 23 msgid "Translatable legacy model #2" 24 msgstr "Modelo legado traducible #2" 25 26 msgid "Translatable legacy models #2" 27 msgstr "Modelos legados traducibles #2" 28 29 msgid "Translatable legacy models #3" 30 msgstr "Modelos legados traducibles #3" 31 32 msgid "Translatable New-style model #1" 33 msgstr "Modelo moderno traducible #1" 34 35 msgid "Translatable New-style model #2" 36 msgid_plural "Translatable New-style models #2" 37 msgstr[0] "Modelo moderno traducible #2" 38 msgstr[1] "Modelos modernos traducibles #2" 39 40 msgid "Translatable New-style models #3" 41 msgstr "Modelos modernos traducibles #3" -
new file tests/regressiontests/verbose_names/models.py
diff --git a/tests/regressiontests/verbose_names/models.py b/tests/regressiontests/verbose_names/models.py new file mode 100644
- + 1 from django.db import models 2 from django.utils.translation import ugettext_lazy as _, ungettext_lazy 3 4 5 class NoMeta(models.Model): 6 name = models.CharField(max_length=20) 7 8 class LegacyOnlyVerboseName(models.Model): 9 name = models.CharField(max_length=20) 10 11 class Meta: 12 verbose_name = 'Legacy model #1' 13 14 class LegacyBothVNames(models.Model): 15 name = models.CharField(max_length=20) 16 17 class Meta: 18 verbose_name = 'Legacy model #2' 19 verbose_name_plural = 'Legacy models #2' 20 21 class LegacyOnlyVerboseNamePlural(models.Model): 22 name = models.CharField(max_length=20) 23 24 class Meta: 25 verbose_name_plural = 'Legacy models #3' 26 27 class LegacyOnlyVerbosenameIntl(models.Model): 28 name = models.CharField(max_length=20) 29 30 class Meta: 31 verbose_name = _('Translatable legacy model #1') 32 33 class LegacyBothIntl(models.Model): 34 name = models.CharField(max_length=20) 35 36 class Meta: 37 verbose_name = _('Translatable legacy model #2') 38 verbose_name_plural = _('Translatable legacy models #2') 39 40 class LegacyPluralIntl(models.Model): 41 name = models.CharField(max_length=20) 42 43 class Meta: 44 verbose_name_plural = _('Translatable legacy models #3') 45 46 # === Models using new classmethod syntax for verbose names ========== 47 48 class NewstyleSingular(models.Model): 49 name = models.CharField(max_length=20) 50 51 @classmethod 52 def verbose_names(cls, count=1): 53 if count == 1: 54 return 'New-style model #1' 55 56 class NewstyleBoth(models.Model): 57 name = models.CharField(max_length=20) 58 59 @classmethod 60 def verbose_names(cls, count=1): 61 if count == 1: 62 return 'New-style model #2' 63 else: 64 return 'New-style models #2' 65 66 class NewstylePlural(models.Model): 67 name = models.CharField(max_length=20) 68 69 @classmethod 70 def verbose_names(cls, count=1): 71 if count != 1: 72 return 'New-style models #3' 73 74 class NewstyleSingularIntl(models.Model): 75 name = models.CharField(max_length=20) 76 77 @classmethod 78 def verbose_names(cls, count=1): 79 if count == 1: 80 return _('Translatable New-style model #1') 81 82 class NewstyleBothIntl(models.Model): 83 name = models.CharField(max_length=20) 84 85 @classmethod 86 def verbose_names(cls, count=1): 87 return ungettext_lazy('Translatable New-style model #2', 'Translatable New-style models #2', count) 88 89 class NewstylePluralIntl(models.Model): 90 name = models.CharField(max_length=20) 91 92 @classmethod 93 def verbose_names(cls, count=1): 94 if count != 1: 95 return _('Translatable New-style models #3') -
new file tests/regressiontests/verbose_names/tests.py
diff --git a/tests/regressiontests/verbose_names/tests.py b/tests/regressiontests/verbose_names/tests.py new file mode 100644
- + 1 from __future__ import absolute_import 2 3 from django.utils.encoding import force_unicode 4 from django.utils import translation 5 from django.utils.unittest import TestCase 6 7 from .models import (NoMeta, LegacyOnlyVerboseName, LegacyBothVNames, 8 LegacyOnlyVerboseNamePlural, LegacyOnlyVerbosenameIntl, LegacyBothIntl, 9 LegacyPluralIntl, NewstyleSingular, NewstyleBoth, NewstylePlural, 10 NewstyleSingularIntl, NewstyleBothIntl, NewstylePluralIntl) 11 12 13 class LegacyVerboseNameNoI18NTests(TestCase): 14 """ 15 Test we don't disrupt behavior associated with legacy 16 Meta.verbose_name{,_plural} attributes when translation isn't used. 17 """ 18 19 def test_noi18n_no_meta_inner_class(self): 20 # A model without an inner Meta class 21 a = NoMeta.objects.create(name=u'Name') 22 self.assertEqual('no meta', NoMeta._meta.verbose_name) 23 self.assertEqual('no meta', a._meta.verbose_name) 24 # Automatically generated plural form, can be bogus (note the arbitrary 25 # 's' tucked at the end) 26 self.assertEqual('no metas', force_unicode(NoMeta._meta.verbose_name_plural)) 27 self.assertEqual('no metas', force_unicode(a._meta.verbose_name_plural)) 28 29 def test_noi18n_only_verbose_name_option(self): 30 a = LegacyOnlyVerboseName.objects.create(name=u'Name') 31 # The verbose_name we specified 32 self.assertEqual('Legacy model #1', LegacyOnlyVerboseName._meta.verbose_name) 33 self.assertEqual('Legacy model #1', a._meta.verbose_name) 34 # Automatically generated plural form, can be bogus (note the arbitrary 35 # 's' tucked at the end) 36 self.assertEqual('Legacy model #1s', force_unicode(LegacyOnlyVerboseName._meta.verbose_name_plural)) 37 self.assertEqual('Legacy model #1s', force_unicode(a._meta.verbose_name_plural)) 38 39 def test_noi18n_both_verbose_name_options(self): 40 b = LegacyBothVNames.objects.create(name=u'Name') 41 # The verbose_name we specified 42 self.assertEqual('Legacy model #2', LegacyBothVNames._meta.verbose_name) 43 self.assertEqual('Legacy model #2', b._meta.verbose_name) 44 # The verbose_name_plural we specified 45 self.assertEqual('Legacy models #2', LegacyBothVNames._meta.verbose_name_plural) 46 self.assertEqual('Legacy models #2', b._meta.verbose_name_plural) 47 48 def test_noi18n_only_verbose_name_plural_option(self): 49 c = LegacyOnlyVerboseNamePlural.objects.create(name=u'Name') 50 # Verbose name automatically generated from the class name 51 self.assertEqual('legacy only verbose name plural', LegacyOnlyVerboseNamePlural._meta.verbose_name) 52 self.assertEqual('legacy only verbose name plural', c._meta.verbose_name) 53 # The verbose_name_plural we specified 54 self.assertEqual('Legacy models #3', LegacyOnlyVerboseNamePlural._meta.verbose_name_plural) 55 self.assertEqual('Legacy models #3', c._meta.verbose_name_plural) 56 57 58 class LegacyVerboseNameI18NTests(TestCase): 59 """ 60 Test we don't disrupt behavior associated with legacy 61 Meta.verbose_name{,_plural} attributes when translation is used. 62 """ 63 64 def setUp(self): 65 translation.activate('es-ar') 66 67 def tearDown(self): 68 translation.deactivate() 69 70 def test_i18n_no_meta_inner_class(self): 71 # A model without an inner Meta class 72 a = NoMeta.objects.create(name=u'Name') 73 self.assertEqual('no meta', NoMeta._meta.verbose_name) 74 self.assertEqual('no meta', a._meta.verbose_name) 75 # Automatically generated plural form, can be bogus (note the arbitrary 76 # 's' tucked at the end) 77 self.assertEqual('no metas', force_unicode(NoMeta._meta.verbose_name_plural)) 78 self.assertEqual('no metas', force_unicode(a._meta.verbose_name_plural)) 79 80 def test_i18n_only_verbose_name_option(self): 81 a = LegacyOnlyVerbosenameIntl.objects.create(name=u'Name') 82 # The verbose_name we specified 83 self.assertEqual('Modelo legado traducible #1', force_unicode(LegacyOnlyVerbosenameIntl._meta.verbose_name)) 84 self.assertEqual('Modelo legado traducible #1', a._meta.verbose_name) 85 # Automatically generated plural form, can be bogus (note the arbitrary 86 # 's' tucked at the end) 87 self.assertEqual('Modelo legado traducible #1s', force_unicode(LegacyOnlyVerbosenameIntl._meta.verbose_name_plural)) 88 self.assertEqual('Modelo legado traducible #1s', force_unicode(a._meta.verbose_name_plural)) 89 90 def test_i18n_both_verbose_name_options(self): 91 a = LegacyBothIntl.objects.create(name=u'Name') 92 # The verbose_name we specified 93 self.assertEqual('Modelo legado traducible #2', LegacyBothIntl._meta.verbose_name) 94 self.assertEqual('Modelo legado traducible #2', a._meta.verbose_name) 95 # The verbose_name_plural we specified 96 self.assertEqual('Modelos legados traducibles #2', LegacyBothIntl._meta.verbose_name_plural) 97 self.assertEqual('Modelos legados traducibles #2', a._meta.verbose_name_plural) 98 99 def test_i18n_only_verbose_name_plural_option(self): 100 a = LegacyPluralIntl.objects.create(name=u'Name') 101 # Verbose name automatically generated from the class name 102 self.assertEqual('legacy plural intl', LegacyPluralIntl._meta.verbose_name) 103 self.assertEqual('legacy plural intl', a._meta.verbose_name) 104 # The verbose_name_plural we specified 105 self.assertEqual('Modelos legados traducibles #3', LegacyPluralIntl._meta.verbose_name_plural) 106 self.assertEqual('Modelos legados traducibles #3', a._meta.verbose_name_plural) 107 108 109 class VerboseNameNoI18NTests(TestCase): 110 """ 111 Test new verbose_names() model classmethod behavior when translation isn't 112 used. 113 """ 114 115 def test_backward_compatibility(self): 116 """ 117 Test backward compatibility with legacy Meta.verbose_name{,_plural} 118 attributes. 119 """ 120 a = NewstyleSingular.objects.create(name=u'Name') 121 # The verbose_name derived from the verbose_names() method we specified 122 self.assertEqual('New-style model #1', NewstyleSingular._meta.verbose_name) 123 self.assertEqual('New-style model #1', a._meta.verbose_name) 124 # Automatically generated plural form, can be bogus (note the arbitrary 125 # 's' tucked at the end) 126 self.assertEqual('New-style model #1s', force_unicode(NewstyleSingular._meta.verbose_name_plural)) 127 self.assertEqual('New-style model #1s', force_unicode(a._meta.verbose_name_plural)) 128 129 b = NewstyleBoth.objects.create(name=u'Name') 130 # The verbose_name derived from the verbose_names() we specified 131 self.assertEqual('New-style model #2', NewstyleBoth._meta.verbose_name) 132 self.assertEqual('New-style model #2', b._meta.verbose_name) 133 # The verbose_name_plural derived from the verbose_names() method we 134 # specified 135 self.assertEqual('New-style models #2', NewstyleBoth._meta.verbose_name_plural) 136 self.assertEqual('New-style models #2', b._meta.verbose_name_plural) 137 138 c = NewstylePlural.objects.create(name=u'Name') 139 # Verbose name automatically generated from the class name 140 self.assertEqual('newstyle plural', NewstylePlural._meta.verbose_name) 141 self.assertEqual('newstyle plural', c._meta.verbose_name) 142 # The verbose_name_plural derived from the verbose_names() method we 143 # specified 144 self.assertEqual('New-style models #3', NewstylePlural._meta.verbose_name_plural) 145 self.assertEqual('New-style models #3', c._meta.verbose_name_plural) 146 147 def test_new_behavior(self): 148 """ 149 Test sanity of new verbose_names() model classmethod. 150 """ 151 a = NewstyleSingular.objects.create(name=u'Name') 152 self.assertEqual('New-style model #1', NewstyleSingular._meta.get_verbose_name()) 153 self.assertEqual('New-style model #1', a._meta.get_verbose_name()) 154 # Fallback get_verbose_name() implementation, its return value 155 # can be bogus (note the arbitrary 's' tucked at the end) 156 self.assertEqual('New-style model #1s', force_unicode(NewstyleSingular._meta.get_verbose_name(0))) 157 self.assertEqual('New-style model #1s', force_unicode(a._meta.get_verbose_name(0))) 158 159 b = NewstyleBoth.objects.create(name=u'Name') 160 self.assertEqual('New-style model #2', NewstyleBoth._meta.get_verbose_name()) 161 self.assertEqual('New-style model #2', b._meta.get_verbose_name()) 162 163 self.assertEqual('New-style models #2', NewstyleBoth._meta.get_verbose_name(0)) 164 self.assertEqual('New-style models #2', b._meta.get_verbose_name(0)) 165 166 c = NewstylePlural.objects.create(name=u'Name') 167 # Fallback get_verbose_name() implementation: Returns a value 168 # automatically generated from the class name 169 self.assertEqual('newstyle plural', NewstylePlural._meta.get_verbose_name()) 170 self.assertEqual('newstyle plural', c._meta.get_verbose_name()) 171 172 self.assertEqual('New-style models #3', NewstylePlural._meta.get_verbose_name(0)) 173 self.assertEqual('New-style models #3', c._meta.get_verbose_name(0)) 174 175 176 class VerboseNameI18NTests(TestCase): 177 """ 178 Test new verbose_names() model classmethod behavior when translation is 179 used. 180 """ 181 182 def setUp(self): 183 translation.activate('es-ar') 184 185 def tearDown(self): 186 translation.deactivate() 187 188 def test_backward_compatibility(self): 189 """ 190 Test backward compatibility with legacy Meta.verbose_name{,_plural} 191 attributes. 192 """ 193 a = NewstyleSingularIntl.objects.create(name=u'Name') 194 # The verbose_name derived from the verbose_names() method we specified 195 self.assertEqual('Modelo moderno traducible #1', NewstyleSingularIntl._meta.verbose_name) 196 self.assertEqual('Modelo moderno traducible #1', a._meta.verbose_name) 197 # Automatically generated plural form, can be bogus (note the arbitrary 198 # 's' tucked at the end) 199 self.assertEqual('Modelo moderno traducible #1s', force_unicode(NewstyleSingularIntl._meta.verbose_name_plural)) 200 self.assertEqual('Modelo moderno traducible #1s', force_unicode(a._meta.verbose_name_plural)) 201 202 b = NewstyleBothIntl.objects.create(name=u'Name') 203 # The verbose_name derived from the verbose_names() we specified 204 self.assertEqual('Modelo moderno traducible #2', force_unicode(NewstyleBothIntl._meta.verbose_name)) 205 self.assertEqual('Modelo moderno traducible #2', b._meta.verbose_name) 206 # The verbose_name_plural derived from the verbose_names() method we 207 # specified 208 self.assertEqual('Modelos modernos traducibles #2', NewstyleBothIntl._meta.verbose_name_plural) 209 self.assertEqual('Modelos modernos traducibles #2', b._meta.verbose_name_plural) 210 211 c = NewstylePluralIntl.objects.create(name=u'Name') 212 # Verbose name automatically generated from the class name -- untranslatable 213 self.assertEqual('newstyle plural intl', NewstylePluralIntl._meta.verbose_name) 214 self.assertEqual('newstyle plural intl', c._meta.verbose_name) 215 # The verbose_name_plural derived from the verbose_names() method we 216 # specified 217 self.assertEqual('Modelos modernos traducibles #3', NewstylePluralIntl._meta.verbose_name_plural) 218 self.assertEqual('Modelos modernos traducibles #3', c._meta.verbose_name_plural) 219 220 def test_new_behavior(self): 221 """ 222 Test sanity of new verbose_names() model classmethod. 223 """ 224 a = NewstyleSingularIntl.objects.create(name=u'Name') 225 self.assertEqual('Modelo moderno traducible #1', NewstyleSingularIntl._meta.get_verbose_name()) 226 self.assertEqual('Modelo moderno traducible #1', a._meta.get_verbose_name()) 227 # Fallback get_verbose_name() implementation, its return value 228 # can be bogus (note the arbitrary 's' tucked at the end) 229 self.assertEqual('Modelo moderno traducible #1s', force_unicode(NewstyleSingularIntl._meta.get_verbose_name(0))) 230 self.assertEqual('Modelo moderno traducible #1s', force_unicode(a._meta.get_verbose_name(0))) 231 232 b = NewstyleBothIntl.objects.create(name=u'Name') 233 self.assertEqual('Modelo moderno traducible #2', NewstyleBothIntl._meta.get_verbose_name()) 234 self.assertEqual('Modelo moderno traducible #2', b._meta.get_verbose_name()) 235 236 self.assertEqual('Modelos modernos traducibles #2', NewstyleBothIntl._meta.get_verbose_name(0)) 237 self.assertEqual('Modelos modernos traducibles #2', b._meta.get_verbose_name(0)) 238 239 c = NewstylePluralIntl.objects.create(name=u'Name') 240 # Fallback get_verbose_name() implementation: Returns a value 241 # automatically generated from the class name -- untranslatable 242 self.assertEqual('newstyle plural intl', NewstylePluralIntl._meta.get_verbose_name()) 243 self.assertEqual('newstyle plural intl', c._meta.get_verbose_name()) 244 245 self.assertEqual('Modelos modernos traducibles #3', NewstylePluralIntl._meta.get_verbose_name(0)) 246 self.assertEqual('Modelos modernos traducibles #3', c._meta.get_verbose_name(0))