| 1 | Index: django/contrib/admin/validation.py
|
|---|
| 2 | ===================================================================
|
|---|
| 3 | --- django/contrib/admin/validation.py (revision 12223)
|
|---|
| 4 | +++ django/contrib/admin/validation.py (working copy)
|
|---|
| 5 | @@ -1,5 +1,6 @@
|
|---|
| 6 | from django.core.exceptions import ImproperlyConfigured
|
|---|
| 7 | from django.db import models
|
|---|
| 8 | +from django.db.models.sql.constants import LOOKUP_SEP
|
|---|
| 9 | from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_model,
|
|---|
| 10 | _get_foreign_key)
|
|---|
| 11 | from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin
|
|---|
| 12 | @@ -32,8 +33,11 @@
|
|---|
| 13 | try:
|
|---|
| 14 | opts.get_field(field)
|
|---|
| 15 | except models.FieldDoesNotExist:
|
|---|
| 16 | - raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
|
|---|
| 17 | - % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
|
|---|
| 18 | + try:
|
|---|
| 19 | + get_closest_relation(model, field)
|
|---|
| 20 | + except AttributeError, models.FieldDoesNotExist:
|
|---|
| 21 | + raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
|
|---|
| 22 | + % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
|
|---|
| 23 | else:
|
|---|
| 24 | # getattr(model, field) could be an X_RelatedObjectsDescriptor
|
|---|
| 25 | f = fetch_attr(cls, model, opts, "list_display[%d]" % idx, field)
|
|---|
| 26 | @@ -349,3 +353,28 @@
|
|---|
| 27 | except AttributeError:
|
|---|
| 28 | raise ImproperlyConfigured("'%s.%s' refers to '%s' that is neither a field, method or property of model '%s'."
|
|---|
| 29 | % (cls.__name__, label, field, model.__name__))
|
|---|
| 30 | +
|
|---|
| 31 | +def get_closest_relation(model,relation):
|
|---|
| 32 | + # Recursively go down all forward relations, then all backward relations with this model to see if the relation exists.
|
|---|
| 33 | + if not LOOKUP_SEP in relation:
|
|---|
| 34 | + if hasattr(model,'base') and relation in model.base.field.rel.to._meta.get_all_field_names():
|
|---|
| 35 | + """
|
|---|
| 36 | + If this model has a base class and the field is really on it,
|
|---|
| 37 | + return the actual base class.
|
|---|
| 38 | + """
|
|---|
| 39 | + model = model.base.field.rel.to
|
|---|
| 40 | + return model._meta.get_field_by_name(relation) # at this point we should have our field, or it raises FieldDoesNotExist
|
|---|
| 41 | +
|
|---|
| 42 | + this_module = relation.split(LOOKUP_SEP,1)
|
|---|
| 43 | +
|
|---|
| 44 | + if this_module[0] in model._meta.get_all_field_names(): # forward relations
|
|---|
| 45 | + rel = model._meta.get_field_by_name(this_module[0])[0].rel
|
|---|
| 46 | + if isinstance(rel,models.fields.related.OneToOneRel) or isinstance(rel,models.fields.related.ManyToOneRel):
|
|---|
| 47 | + return get_closest_relation(rel.to,this_module[1])
|
|---|
| 48 | +
|
|---|
| 49 | + for rel in model._meta.get_all_related_objects(): # backward relations
|
|---|
| 50 | + if this_module[0] in (rel.name, rel.field.related_query_name) and isinstance(rel,models.fields.related.OneToOneRel):
|
|---|
| 51 | + return get_closest_relation(rel.model,this_module[1])
|
|---|
| 52 | +
|
|---|
| 53 | + raise AttributeError("%s relation not found on %s." % (this_module[0],model._meta.verbose_name))
|
|---|
| 54 | Index: django/contrib/admin/util.py
|
|---|
| 55 | ===================================================================
|
|---|
| 56 | --- django/contrib/admin/util.py (revision 12223)
|
|---|
| 57 | +++ django/contrib/admin/util.py (working copy)
|
|---|
| 58 | @@ -1,5 +1,6 @@
|
|---|
| 59 | from django.core.exceptions import ObjectDoesNotExist
|
|---|
| 60 | from django.db import models
|
|---|
| 61 | +from django.db.models.sql.constants import LOOKUP_SEP
|
|---|
| 62 | from django.utils import formats
|
|---|
| 63 | from django.utils.html import escape
|
|---|
| 64 | from django.utils.safestring import mark_safe
|
|---|
| 65 | @@ -229,9 +230,13 @@
|
|---|
| 66 | try:
|
|---|
| 67 | f = opts.get_field(name)
|
|---|
| 68 | except models.FieldDoesNotExist:
|
|---|
| 69 | - # For non-field values, the value is either a method, property or
|
|---|
| 70 | - # returned via a callable.
|
|---|
| 71 | - if callable(name):
|
|---|
| 72 | + # For non-field values, the value is either a relation, method, property or
|
|---|
| 73 | + # returned via a callable
|
|---|
| 74 | + if type(name) == str and LOOKUP_SEP in name:
|
|---|
| 75 | + value = obj
|
|---|
| 76 | + for attr in name.split(LOOKUP_SEP):
|
|---|
| 77 | + value = getattr(value,attr)
|
|---|
| 78 | + elif callable(name):
|
|---|
| 79 | attr = name
|
|---|
| 80 | value = attr(obj)
|
|---|
| 81 | elif (model_admin is not None and hasattr(model_admin, name) and
|
|---|
| 82 | @@ -259,6 +264,8 @@
|
|---|
| 83 | label = force_unicode(model._meta.verbose_name)
|
|---|
| 84 | elif name == "__str__":
|
|---|
| 85 | label = smart_str(model._meta.verbose_name)
|
|---|
| 86 | + elif type(name) == str and LOOKUP_SEP in name:
|
|---|
| 87 | + label = name.split(LOOKUP_SEP)[-1].replace('_',' ')
|
|---|
| 88 | else:
|
|---|
| 89 | if callable(name):
|
|---|
| 90 | attr = name
|
|---|
| 91 | @@ -286,7 +293,6 @@
|
|---|
| 92 | else:
|
|---|
| 93 | return label
|
|---|
| 94 |
|
|---|
| 95 | -
|
|---|
| 96 | def display_for_field(value, field):
|
|---|
| 97 | from django.contrib.admin.templatetags.admin_list import _boolean_icon
|
|---|
| 98 | from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
|
|---|
| 99 |
|
|---|