Django

Code

Changeset 8352

Show
Ignore:
Timestamp:
08/14/08 15:12:19 (11 months ago)
Author:
brosner
Message:

Fixed #7503 -- Allow callables in list_display. This also does a lookup on the ModelAdmin? for the method if the value is a string before looking on the model. Refs #8054. Thanks qmanic and Daniel Pope for tickets and patches.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/admin/templatetags/admin_list.py

    r7967 r8352  
    8585                header = smart_str(lookup_opts.verbose_name) 
    8686            else: 
    87                 attr = getattr(cl.model, field_name) # Let AttributeErrors propagate. 
     87                if callable(field_name): 
     88                    attr = field_name # field_name can be a callable 
     89                else: 
     90                    try: 
     91                        attr = getattr(cl.model_admin, field_name) 
     92                    except AttributeError: 
     93                        try: 
     94                            attr = getattr(cl.model, field_name) 
     95                        except AttributeError: 
     96                            raise AttributeError, \ 
     97                                "'%s' model or '%s' objects have no attribute '%s'" % \ 
     98                                    (lookup_opts.object_name, cl.model_admin.__class__, field_name) 
     99                 
    88100                try: 
    89101                    header = attr.short_description 
    90102                except AttributeError: 
    91                     header = field_name.replace('_', ' ') 
     103                    if callable(field_name): 
     104                        header = field_name.__name__ 
     105                    else: 
     106                        header = field_name 
     107                    header = header.replace('_', ' ') 
    92108 
    93109            # It is a non-field, but perhaps one that is sortable 
    94             admin_order_field = getattr(getattr(cl.model, field_name), "admin_order_field", None) 
     110            admin_order_field = getattr(attr, "admin_order_field", None) 
    95111            if not admin_order_field: 
    96112                yield {"text": header} 
     
    129145            f = cl.lookup_opts.get_field(field_name) 
    130146        except models.FieldDoesNotExist: 
    131             # For non-field list_display values, the value is either a method 
    132             # or a property
     147            # For non-field list_display values, the value is either a method, 
     148            # property or returned via a callable
    133149            try: 
    134                 attr = getattr(result, field_name) 
     150                if callable(field_name): 
     151                    attr = field_name 
     152                    value = attr(result) 
     153                elif hasattr(cl.model_admin, field_name): 
     154                    attr = getattr(cl.model_admin, field_name) 
     155                    value = attr(result) 
     156                else: 
     157                    attr = getattr(result, field_name) 
     158                    if callable(attr): 
     159                        value = attr() 
     160                    else: 
     161                        value = attr 
    135162                allow_tags = getattr(attr, 'allow_tags', False) 
    136163                boolean = getattr(attr, 'boolean', False) 
    137                 if callable(attr): 
    138                     attr = attr() 
    139164                if boolean: 
    140165                    allow_tags = True 
    141                     result_repr = _boolean_icon(attr
    142                 else: 
    143                     result_repr = smart_unicode(attr
     166                    result_repr = _boolean_icon(value
     167                else: 
     168                    result_repr = smart_unicode(value
    144169            except (AttributeError, ObjectDoesNotExist): 
    145170                result_repr = EMPTY_CHANGELIST_VALUE 
  • django/trunk/django/contrib/admin/validation.py

    r8173 r8352  
    3636        _check_istuplew('list_display', cls.list_display) 
    3737        for idx, field in enumerate(cls.list_display): 
    38             f = _check_attr_existsw("list_display[%d]" % idx, field) 
    39             if isinstance(f, models.ManyToManyField): 
    40                 raise ImproperlyConfigured("`%s.list_display[%d]`, `%s` is a " 
    41                         "ManyToManyField which is not supported." 
    42                         % (cls.__name__, idx, field)) 
     38            if not callable(field): 
     39                if not hasattr(cls, field): 
     40                    if not hasattr(model, field): 
     41                        try: 
     42                            return opts.get_field(field) 
     43                        except models.FieldDoesNotExist: 
     44                            raise ImproperlyConfigured("%s.list_display[%d], %r is " 
     45                                "not a callable or an attribute of %r or found in the model %r." 
     46                                % (cls.__name__, idx, field, cls.__name__, model._meta.object_name)) 
     47                        f = _check_attr_existsw("list_display[%d]" % idx, field) 
     48                        if isinstance(f, models.ManyToManyField): 
     49                            raise ImproperlyConfigured("`%s.list_display[%d]`, `%s` is a " 
     50                                "ManyToManyField which is not supported." 
     51                                % (cls.__name__, idx, field)) 
    4352 
    4453    # list_display_links 
  • django/trunk/docs/admin.txt

    r8324 r8352  
    202202that displays the ``__unicode__()`` representation of each object. 
    203203 
     204You have four possible values that can be used in ``list_display``: 
     205 
     206    * A field of the model. For example:: 
     207     
     208          class PersonAdmin(admin.ModelAdmin): 
     209              list_display = ('first_name', 'last_name') 
     210     
     211    * A callable that accepts one parameter for the model instance. For 
     212      example:: 
     213     
     214          def upper_case_name(obj): 
     215              return "%s %s" % (obj.first_name, obj.last_name).upper() 
     216          upper_case_name.short_description = 'Name' 
     217         
     218          class PersonAdmin(admin.ModelAdmin): 
     219              list_display = (upper_case_name,) 
     220     
     221    * A string representating an attribute on the ``ModelAdmin``. This behaves 
     222      the same as the callable. For example:: 
     223       
     224          class PersonAdmin(admin.ModelAdmin): 
     225              list_display = ('upper_case_name',) 
     226               
     227              def upper_case_name(self, obj): 
     228                return "%s %s" % (obj.first_name, obj.last_name).upper() 
     229              upper_case_name.short_description = 'Name' 
     230     
     231    * A string representating an attribute on the model. This behaves almost 
     232      the same as the callable, but ``self`` in this context is the model 
     233      instance. Here's a full model example:: 
     234 
     235          class Person(models.Model): 
     236              name = models.CharField(max_length=50) 
     237              birthday = models.DateField() 
     238 
     239              def decade_born_in(self): 
     240                  return self.birthday.strftime('%Y')[:3] + "0's" 
     241              decade_born_in.short_description = 'Birth decade' 
     242 
     243          class PersonAdmin(admin.ModelAdmin): 
     244              list_display = ('name', 'decade_born_in') 
     245 
    204246A few special cases to note about ``list_display``: 
    205247 
     
    216258      display a pretty "on" or "off" icon instead of ``True`` or ``False``. 
    217259 
    218     * If the string given is a method of the model, Django will call it and 
    219       display the output. This method should have a ``short_description`` 
    220       function attribute, for use as the header for the field. 
    221  
    222       Here's a full example model:: 
    223  
    224           class Person(models.Model): 
    225               name = models.CharField(max_length=50) 
    226               birthday = models.DateField() 
    227  
    228               def decade_born_in(self): 
    229                   return self.birthday.strftime('%Y')[:3] + "0's" 
    230               decade_born_in.short_description = 'Birth decade' 
    231  
    232           class PersonAdmin(admin.ModelAdmin): 
    233               list_display = ('name', 'decade_born_in') 
    234  
    235     * If the string given is a method of the model, Django will HTML-escape the 
    236       output by default. If you'd rather not escape the output of the method, 
    237       give the method an ``allow_tags`` attribute whose value is ``True``. 
    238  
     260    * If the string given is a method of the model, ``ModelAdmin`` or a 
     261      callable, Django will HTML-escape the output by default. If you'd rather 
     262      not escape the output of the method, give the method an ``allow_tags`` 
     263      attribute whose value is ``True``. 
     264       
    239265      Here's a full example model:: 
    240266 
     
    251277              list_display = ('first_name', 'last_name', 'colored_name') 
    252278 
    253     * If the string given is a method of the model that returns True or False 
    254       Django will display a pretty "on" or "off" icon if you give the method a 
    255       ``boolean`` attribute whose value is ``True``. 
     279    * If the string given is a method of the model, ``ModelAdmin`` or a 
     280      callable that returns True or False Django will display a pretty "on" or 
     281      "off" icon if you give the method a ``boolean`` attribute whose value is 
     282      ``True``. 
    256283 
    257284      Here's a full example model::