Index: django/contrib/admin/validation.py
===================================================================
--- django/contrib/admin/validation.py	(revision 12223)
+++ django/contrib/admin/validation.py	(working copy)
@@ -1,5 +1,6 @@
 from django.core.exceptions import ImproperlyConfigured
 from django.db import models
+from django.db.models.sql.constants import LOOKUP_SEP
 from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_model,
     _get_foreign_key)
 from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin
@@ -32,8 +33,11 @@
                         try:
                             opts.get_field(field)
                         except models.FieldDoesNotExist:
-                            raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
-                                % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
+                            try:
+                                get_closest_relation(model, field)
+                            except AttributeError, models.FieldDoesNotExist:
+                                raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
+                                    % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
                     else:
                         # getattr(model, field) could be an X_RelatedObjectsDescriptor
                         f = fetch_attr(cls, model, opts, "list_display[%d]" % idx, field)
@@ -349,3 +353,28 @@
     except AttributeError:
         raise ImproperlyConfigured("'%s.%s' refers to '%s' that is neither a field, method or property of model '%s'."
             % (cls.__name__, label, field, model.__name__))
+
+def get_closest_relation(model,relation):
+   # Recursively go down all forward relations, then all backward relations with this model to see if the relation exists.
+   if not LOOKUP_SEP in relation:
+       if hasattr(model,'base') and relation in model.base.field.rel.to._meta.get_all_field_names():
+           """
+           If this model has a base class and the field is really on it, 
+           return the actual base class.
+           """
+           model = model.base.field.rel.to
+       return model._meta.get_field_by_name(relation) # at this point we should have our field, or it raises FieldDoesNotExist
+
+   this_module = relation.split(LOOKUP_SEP,1)
+
+   if this_module[0] in model._meta.get_all_field_names(): # forward relations
+       rel = model._meta.get_field_by_name(this_module[0])[0].rel
+       if isinstance(rel,models.fields.related.OneToOneRel) or isinstance(rel,models.fields.related.ManyToOneRel):
+           return get_closest_relation(rel.to,this_module[1])
+
+   for rel in model._meta.get_all_related_objects(): # backward relations
+       if this_module[0] in (rel.name, rel.field.related_query_name) and isinstance(rel,models.fields.related.OneToOneRel):
+           return get_closest_relation(rel.model,this_module[1])
+
+   raise AttributeError("%s relation not found on %s." % (this_module[0],model._meta.verbose_name))
Index: django/contrib/admin/util.py
===================================================================
--- django/contrib/admin/util.py	(revision 12223)
+++ django/contrib/admin/util.py	(working copy)
@@ -1,5 +1,6 @@
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
+from django.db.models.sql.constants import LOOKUP_SEP
 from django.utils import formats
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
@@ -229,9 +230,13 @@
     try:
         f = opts.get_field(name)
     except models.FieldDoesNotExist:
-        # For non-field values, the value is either a method, property or
-        # returned via a callable.
-        if callable(name):
+        # For non-field values, the value is either a relation, method, property or
+        # returned via a callable
+        if type(name) == str and LOOKUP_SEP in name:
+            value = obj
+            for attr in name.split(LOOKUP_SEP):
+                value = getattr(value,attr)
+        elif callable(name):
             attr = name
             value = attr(obj)
         elif (model_admin is not None and hasattr(model_admin, name) and
@@ -259,6 +264,8 @@
             label = force_unicode(model._meta.verbose_name)
         elif name == "__str__":
             label = smart_str(model._meta.verbose_name)
+        elif type(name) == str and LOOKUP_SEP in name:
+            label = name.split(LOOKUP_SEP)[-1].replace('_',' ')
         else:
             if callable(name):
                 attr = name
@@ -286,7 +293,6 @@
     else:
         return label
 
-
 def display_for_field(value, field):
     from django.contrib.admin.templatetags.admin_list import _boolean_icon
     from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
