فهرست منبع

Fixed #24693 -- Added label and label_lower property to Model._meta

Luis Del Giudice 10 سال پیش
والد
کامیت
69ddc1b3da

+ 1 - 3
django/core/checks/messages.py

@@ -37,9 +37,7 @@ class CheckMessage(object):
         elif isinstance(self.obj, models.base.ModelBase):
             # We need to hardcode ModelBase and Field cases because its __str__
             # method doesn't return "applabel.modellabel" and cannot be changed.
-            model = self.obj
-            app = model._meta.app_label
-            obj = '%s.%s' % (app, model._meta.object_name)
+            obj = self.obj._meta.label
         else:
             obj = force_str(self.obj)
         id = "(%s) " % self.id if self.id else ""

+ 2 - 2
django/core/serializers/base.py

@@ -163,8 +163,8 @@ class DeserializedObject(object):
         self.m2m_data = m2m_data
 
     def __repr__(self):
-        return "<DeserializedObject: %s.%s(pk=%s)>" % (
-            self.object._meta.app_label, self.object._meta.object_name, self.object.pk)
+        return "<DeserializedObject: %s(pk=%s)>" % (
+            self.object._meta.label, self.object.pk)
 
     def save(self, save_m2m=True, using=None):
         # Call save on the Model baseclass directly. This bypasses any

+ 3 - 4
django/db/migrations/state.py

@@ -362,10 +362,9 @@ class ModelState(object):
             try:
                 fields.append((name, field_class(*args, **kwargs)))
             except TypeError as e:
-                raise TypeError("Couldn't reconstruct field %s on %s.%s: %s" % (
+                raise TypeError("Couldn't reconstruct field %s on %s: %s" % (
                     name,
-                    model._meta.app_label,
-                    model._meta.object_name,
+                    model._meta.label,
                     e,
                 ))
         if not exclude_rels:
@@ -423,7 +422,7 @@ class ModelState(object):
         # Make our record
         bases = tuple(
             (
-                "%s.%s" % (base._meta.app_label, base._meta.model_name)
+                base._meta.label_lower
                 if hasattr(base, "_meta") else
                 base
             )

+ 2 - 7
django/db/models/base.py

@@ -326,9 +326,7 @@ class ModelBase(type):
         if cls.__doc__ is None:
             cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join(f.name for f in opts.fields))
 
-        get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get(
-            '%s.%s' % (opts.app_label, opts.model_name)
-        )
+        get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get(opts.label_lower)
         if get_absolute_url_override:
             setattr(cls, 'get_absolute_url', get_absolute_url_override)
 
@@ -1248,10 +1246,7 @@ class Model(six.with_metaclass(ModelBase)):
                 errors.append(
                     checks.Error(
                         "The model has two many-to-many relations through "
-                        "the intermediate model '%s.%s'." % (
-                            f.remote_field.through._meta.app_label,
-                            f.remote_field.through._meta.object_name
-                        ),
+                        "the intermediate model '%s'." % f.remote_field.through._meta.label,
                         hint=None,
                         obj=cls,
                         id='models.E003',

+ 1 - 3
django/db/models/manager.py

@@ -76,9 +76,7 @@ class BaseManager(object):
 
     def __str__(self):
         """ Return "app_label.model_label.manager_name". """
-        model = self.model
-        app = model._meta.app_label
-        return '%s.%s.%s' % (app, model._meta.object_name, self.name)
+        return '%s.%s' % (self.model._meta.label, self.name)
 
     def deconstruct(self):
         """

+ 9 - 2
django/db/models/options.py

@@ -176,6 +176,14 @@ class Options(object):
         m2m = link.is_relation and link.many_to_many
         return link, model, direct, m2m
 
+    @property
+    def label(self):
+        return '%s.%s' % (self.app_label, self.object_name)
+
+    @property
+    def label_lower(self):
+        return '%s.%s' % (self.app_label, self.model_name)
+
     @property
     def app_config(self):
         # Don't go through get_app_config to avoid triggering imports.
@@ -377,7 +385,6 @@ class Options(object):
         case insensitive, so we make sure we are case insensitive here.
         """
         if self.swappable:
-            model_label = '%s.%s' % (self.app_label, self.model_name)
             swapped_for = getattr(settings, self.swappable, None)
             if swapped_for:
                 try:
@@ -389,7 +396,7 @@ class Options(object):
                     # or as part of validation.
                     return swapped_for
 
-                if '%s.%s' % (swapped_label, swapped_object.lower()) not in (None, model_label):
+                if '%s.%s' % (swapped_label, swapped_object.lower()) not in (None, self.label_lower):
                     return swapped_for
         return None
 

+ 10 - 14
django/forms/models.py

@@ -973,12 +973,12 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
                     (fk.remote_field.model != parent_model and
                      fk.remote_field.model not in parent_model._meta.get_parent_list()):
                 raise ValueError(
-                    "fk_name '%s' is not a ForeignKey to '%s.%s'."
-                    % (fk_name, parent_model._meta.app_label, parent_model._meta.object_name))
+                    "fk_name '%s' is not a ForeignKey to '%s'." % (fk_name, parent_model._meta.label)
+                )
         elif len(fks_to_parent) == 0:
             raise ValueError(
-                "'%s.%s' has no field named '%s'."
-                % (model._meta.app_label, model._meta.object_name, fk_name))
+                "'%s' has no field named '%s'." % (model._meta.label, fk_name)
+            )
     else:
         # Try to discover what the ForeignKey from model to parent_model is
         fks_to_parent = [
@@ -993,20 +993,16 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
             if can_fail:
                 return
             raise ValueError(
-                "'%s.%s' has no ForeignKey to '%s.%s'." % (
-                    model._meta.app_label,
-                    model._meta.object_name,
-                    parent_model._meta.app_label,
-                    parent_model._meta.object_name,
+                "'%s' has no ForeignKey to '%s'." % (
+                    model._meta.label,
+                    parent_model._meta.label,
                 )
             )
         else:
             raise ValueError(
-                "'%s.%s' has more than one ForeignKey to '%s.%s'." % (
-                    model._meta.app_label,
-                    model._meta.object_name,
-                    parent_model._meta.app_label,
-                    parent_model._meta.object_name,
+                "'%s' has more than one ForeignKey to '%s'." % (
+                    model._meta.label,
+                    parent_model._meta.label,
                 )
             )
     return fk

+ 29 - 0
docs/ref/models/options.txt

@@ -29,6 +29,12 @@ Available ``Meta`` options
 
         app_label = 'myapp'
 
+    .. versionadded:: 1.9
+
+    If you want to represent a model with the format ``app_label.object_name``
+    or ``app_label.model_name`` you can use ``model._meta.label``
+    or ``model._meta.label_lower`` respectively.
+
 ``db_table``
 ------------
 
@@ -397,3 +403,26 @@ Django quotes column and table names behind the scenes.
         verbose_name_plural = "stories"
 
     If this isn't given, Django will use :attr:`~Options.verbose_name` + ``"s"``.
+
+Read-only ``Meta`` attributes
+=============================
+
+``label``
+---------
+
+.. attribute:: Options.label
+
+    .. versionadded:: 1.9
+
+    Representation of the object, returns ``app_label.object_name``, e.g.
+    ``'polls.Question'``.
+
+``label_lower``
+---------------
+
+.. attribute:: Options.label_lower
+
+    .. versionadded:: 1.9
+
+    Representation of the model, returns ``app_label.model_name``, e.g.
+    ``'polls.question'``.

+ 12 - 0
tests/model_meta/results.py

@@ -791,4 +791,16 @@ TEST_RESULTS = {
             'content_object_abstract',
         ],
     },
+    'labels': {
+        AbstractPerson: 'model_meta.AbstractPerson',
+        BasePerson: 'model_meta.BasePerson',
+        Person: 'model_meta.Person',
+        Relating: 'model_meta.Relating',
+    },
+    'lower_labels': {
+        AbstractPerson: 'model_meta.abstractperson',
+        BasePerson: 'model_meta.baseperson',
+        Person: 'model_meta.person',
+        Relating: 'model_meta.relating',
+    },
 }

+ 11 - 0
tests/model_meta/tests.py

@@ -49,6 +49,17 @@ class GetFieldsTests(OptionsBaseTests):
                 fields += ["errors"]
 
 
+class LabelTests(OptionsBaseTests):
+
+    def test_label(self):
+        for model, expected_result in TEST_RESULTS['labels'].items():
+            self.assertEqual(model._meta.label, expected_result)
+
+    def test_label_lower(self):
+        for model, expected_result in TEST_RESULTS['lower_labels'].items():
+            self.assertEqual(model._meta.label_lower, expected_result)
+
+
 class DataTests(OptionsBaseTests):
 
     def test_fields(self):