Ticket #11058: django-11058-list_display_links.patch

File django-11058-list_display_links.patch, 5.9 KB (added by Chris Adams, 13 years ago)
  • django/contrib/admin/validation.py

    From 10a04188c0d002509507a4e77756742f8729379d Mon Sep 17 00:00:00 2001
    From: Chris Adams <chris@improbable.org>
    Date: Thu, 23 Dec 2010 10:30:27 -0500
    Subject: [PATCH] ModelAdmin validation: allow non-model fields in list_display_links (#11058)
    
    In addition to model field names, list_display accepts callables, ModelAdmin
    attributes or Model attributes.
    
    This patch reuses the list_display resolution for these values for
    list_display_links validation as well and adds a test for each of the three
    extra types.
    
    See http://code.djangoproject.com/ticket/11058
    ---
     django/contrib/admin/validation.py              |   18 +++++++++++++++---
     docs/ref/contrib/admin/index.txt                |   10 +++++-----
     tests/regressiontests/admin_validation/tests.py |   22 ++++++++++++++++++++++
     3 files changed, 42 insertions(+), 8 deletions(-)
    
    diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
    index bee2891..9a653ea 100644
    a b def validate(cls, model):  
    2222    opts = model._meta
    2323    validate_base(cls, model)
    2424
     25    # To handle values other than pure strings, we'll maintain a list of
     26    # possible values allowing the possibility of using ModelAdmin fields
     27    # or arbitrary callables in list_display_links.
     28    list_display_fields = set()
     29
    2530    # list_display
    2631    if hasattr(cls, 'list_display'):
    2732        check_isseq(cls, 'list_display', cls.list_display)
    2833        for idx, field in enumerate(cls.list_display):
     34            list_display_fields.add(field)
     35
    2936            if not callable(field):
    3037                if not hasattr(cls, field):
    3138                    if not hasattr(model, field):
    3239                        try:
    33                             opts.get_field(field)
     40                            f = opts.get_field(field)
    3441                        except models.FieldDoesNotExist:
    3542                            raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
    3643                                % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
    3744                    else:
    3845                        # getattr(model, field) could be an X_RelatedObjectsDescriptor
    3946                        f = fetch_attr(cls, model, opts, "list_display[%d]" % idx, field)
     47
    4048                        if isinstance(f, models.ManyToManyField):
    4149                            raise ImproperlyConfigured("'%s.list_display[%d]', '%s' is a ManyToManyField which is not supported."
    4250                                % (cls.__name__, idx, field))
    4351
     52                    list_display_fields.add(f)
     53
    4454    # list_display_links
    4555    if hasattr(cls, 'list_display_links'):
    4656        check_isseq(cls, 'list_display_links', cls.list_display_links)
     57
    4758        for idx, field in enumerate(cls.list_display_links):
    48             fetch_attr(cls, model, opts, 'list_display_links[%d]' % idx, field)
    49             if field not in cls.list_display:
     59            if field not in list_display_fields:
    5060                raise ImproperlyConfigured("'%s.list_display_links[%d]'"
    5161                        "refers to '%s' which is not defined in 'list_display'."
    5262                        % (cls.__name__, idx, field))
    5363
     64    del list_display_fields
     65
    5466    # list_filter
    5567    if hasattr(cls, 'list_filter'):
    5668        check_isseq(cls, 'list_filter', cls.list_filter)
  • docs/ref/contrib/admin/index.txt

    diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
    index da17ed0..03c5706 100644
    a b be linked to the "change" page for an object.  
    405405By default, the change list page will link the first column -- the first field
    406406specified in ``list_display`` -- to the change page for each item. But
    407407``list_display_links`` lets you change which columns are linked. Set
    408 ``list_display_links`` to a list or tuple of field names (in the same format as
    409 ``list_display``) to link.
     408``list_display_links`` to a list or tuple of fields in the same format as
     409``list_display`` to link.
    410410
    411 ``list_display_links`` can specify one or many field names. As long as the
    412 field names appear in ``list_display``, Django doesn't care how many (or how
    413 few) fields are linked. The only requirement is: If you want to use
     411``list_display_links`` can specify one or many fields. As long as the fields
     412appear in ``list_display``, Django doesn't care how many (or how few) fields
     413are linked. The only requirement is: If you want to use
    414414``list_display_links``, you must define ``list_display``.
    415415
    416416In this example, the ``first_name`` and ``last_name`` fields will be linked on
  • tests/regressiontests/admin_validation/tests.py

    diff --git a/tests/regressiontests/admin_validation/tests.py b/tests/regressiontests/admin_validation/tests.py
    index 1872ca5..207933b 100644
    a b class ValidationTestCase(TestCase):  
    242242
    243243        validate(FieldsOnFormOnlyAdmin, Song)
    244244
     245    def test_list_display_links(self):
     246        """
     247        Confirm that list_display_links supports model fields, model admin
     248        methods and arbitrary callables
     249        """
     250
     251        def fancy_formatter(obj):
     252            return "Custom display of %s" % obj
    245253
     254        class SongAdmin(admin.ModelAdmin):
     255            foo_date = fancy_formatter
     256
     257            list_display = ("title", "album", "original_release",
     258                            "readonly_method_on_model", "short_title",
     259                            foo_date)
     260            list_display_links = ("title", "album", "original_release",
     261                                    "readonly_method_on_model", "short_title",
     262                                    foo_date)
    246263
     264            def short_title(self, obj):
     265                # In real code this would use truncatewords/chars:
     266                return obj.title[:20]
     267            short_title.short_description = "Short Title"
    247268
     269        validate(SongAdmin, Song)
Back to Top