Commit fb48eb05 authored by Daniel Pyrathon's avatar Daniel Pyrathon Committed by Tim Graham
Browse files

Fixed #12663 -- Formalized the Model._meta API for retrieving fields.

Thanks to Russell Keith-Magee for mentoring this Google Summer of
Code 2014 project and everyone else who helped with the patch!
parent 749d2325
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -337,7 +337,12 @@ class Apps(object):

        This is mostly used in tests.
        """
        # Call expire cache on each model. This will purge
        # the relation tree and the fields cache.
        self.get_models.cache_clear()
        if self.ready:
            for model in self.get_models(include_auto_created=True):
                model._meta._expire_cache()

    ### DEPRECATED METHODS GO BELOW THIS LINE ###

+1 −1
Original line number Diff line number Diff line
@@ -762,7 +762,7 @@ class ModelAdminChecks(BaseModelAdminChecks):

    def _check_list_editable_item(self, cls, model, field_name, label):
        try:
            field = model._meta.get_field_by_name(field_name)[0]
            field = model._meta.get_field(field_name)
        except FieldDoesNotExist:
            return refer_to_missing_field(field=field_name, option=label,
                                          model=model, obj=cls, id='admin.E121')
+8 −5
Original line number Diff line number Diff line
@@ -406,7 +406,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
        rel_name = None
        for part in parts[:-1]:
            try:
                field, _, _, _ = model._meta.get_field_by_name(part)
                field = model._meta.get_field(part)
            except FieldDoesNotExist:
                # Lookups on non-existent fields are ok, since they're ignored
                # later.
@@ -422,7 +422,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
                else:
                    rel_name = None
            elif isinstance(field, ForeignObjectRel):
                model = field.model
                model = field.related_model
                rel_name = model._meta.pk.name
            else:
                rel_name = None
@@ -473,9 +473,12 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
            for inline in admin.inlines:
                registered_models.add(inline.model)

        for related_object in (opts.get_all_related_objects(include_hidden=True) +
                               opts.get_all_related_many_to_many_objects()):
            related_model = related_object.model
        related_objects = (
            f for f in opts.get_fields(include_hidden=True)
            if (f.auto_created and not f.concrete)
        )
        for related_object in related_objects:
            related_model = related_object.related_model
            if (any(issubclass(model, related_model) for model in registered_models) and
                    related_object.field.rel.get_related_field() == field):
                return True
+1 −1
Original line number Diff line number Diff line
@@ -326,7 +326,7 @@ def date_hierarchy(cl):
    """
    if cl.date_hierarchy:
        field_name = cl.date_hierarchy
        field = cl.opts.get_field_by_name(field_name)[0]
        field = cl.opts.get_field(field_name)
        dates_or_datetimes = 'datetimes' if isinstance(field, models.DateTimeField) else 'dates'
        year_field = '%s__year' % field_name
        month_field = '%s__month' % field_name
+21 −9
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ def lookup_needs_distinct(opts, lookup_path):
    Returns True if 'distinct()' should be used to query the given lookup path.
    """
    field_name = lookup_path.split('__', 1)[0]
    field = opts.get_field_by_name(field_name)[0]
    field = opts.get_field(field_name)
    if hasattr(field, 'get_path_info') and any(path.m2m for path in field.get_path_info()):
        return True
    return False
@@ -265,7 +265,7 @@ def model_ngettext(obj, n=None):
def lookup_field(name, obj, model_admin=None):
    opts = obj._meta
    try:
        f = opts.get_field(name)
        f = _get_non_gfk_field(opts, name)
    except FieldDoesNotExist:
        # For non-field values, the value is either a method, property or
        # returned via a callable.
@@ -291,6 +291,17 @@ def lookup_field(name, obj, model_admin=None):
    return f, attr, value


def _get_non_gfk_field(opts, name):
    """
    For historical reasons, the admin app relies on GenericForeignKeys as being
    "not found" by get_field(). This could likely be cleaned up.
    """
    field = opts.get_field(name)
    if field.is_relation and field.one_to_many and not field.related_model:
        raise FieldDoesNotExist()
    return field


def label_for_field(name, model, model_admin=None, return_attr=False):
    """
    Returns a sensible label for a field name. The name can be a callable,
@@ -301,7 +312,7 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
    """
    attr = None
    try:
        field = model._meta.get_field_by_name(name)[0]
        field = _get_non_gfk_field(model._meta, name)
        try:
            label = field.verbose_name
        except AttributeError:
@@ -349,11 +360,10 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
def help_text_for_field(name, model):
    help_text = ""
    try:
        field_data = model._meta.get_field_by_name(name)
        field = _get_non_gfk_field(model._meta, name)
    except FieldDoesNotExist:
        pass
    else:
        field = field_data[0]
        if hasattr(field, 'help_text'):
            help_text = field.help_text
    return smart_text(help_text)
@@ -425,19 +435,21 @@ def reverse_field_path(model, path):
    parent = model
    pieces = path.split(LOOKUP_SEP)
    for piece in pieces:
        field, model, direct, m2m = parent._meta.get_field_by_name(piece)
        field = parent._meta.get_field(piece)
        # skip trailing data field if extant:
        if len(reversed_path) == len(pieces) - 1:  # final iteration
            try:
                get_model_from_relation(field)
            except NotRelationField:
                break
        if direct:

        # Field should point to another model
        if field.is_relation and not (field.auto_created and not field.concrete):
            related_name = field.related_query_name()
            parent = field.rel.to
        else:
            related_name = field.field.name
            parent = field.model
            parent = field.related_model
        reversed_path.insert(0, related_name)
    return (parent, LOOKUP_SEP.join(reversed_path))

@@ -458,7 +470,7 @@ def get_fields_from_path(model, path):
            parent = get_model_from_relation(fields[-1])
        else:
            parent = model
        fields.append(parent._meta.get_field_by_name(piece)[0])
        fields.append(parent._meta.get_field(piece))
    return fields


Loading