wagtail icon indicating copy to clipboard operation
wagtail copied to clipboard

Use same syntax for related objects in SnippetViewSet when overriding colums

Open saevarom opened this issue 7 months ago • 3 comments

Is your proposal related to a problem?

Sometimes we want to override the headings for fields in the list_display attribute and then we have to use the wagtail.admin.ui.tables.Column class. If the associated field is a related field, such as an author of a book, one would use the double underscore __ notation to navigate relations as is the standard in Django Querysets:

class BookAdmin(SnippetViewSet):
    model = Book
    list_display = ["title", "author__name"]

However, when using the Column class, we need to navigate relations using dot-notation:

class BookAdmin(SnippetViewSet):
    model = Book
    list_display = [Column("title", "Book title"), Column("author.name", "Book author")]

Describe the solution you'd like

I expected that when using Column I would have to use the same notation when referencing the related field, which is not the case. It would be best if Column and other related classes would accept the __ syntax for related models.

I think most users would expect the same syntax in both use cases.

Also, the documentation does not mention how to do related fields when using Column.

saevarom avatar Jun 05 '25 15:06 saevarom

I can take this issue but need direction that should we only improve documentation by mentioning this notation or should we actually fix to uniform the notation

Talha-Rizwan avatar Jul 01 '25 13:07 Talha-Rizwan

I'm not sure if this is something we want to do. On the surface it does make sense to make these uniform, but the reason why they differ is because of the implementation and the underlying principles.

  • The tables framework, and thus the Column class which is part of it, can work with any object list, not necessarily model instances. When this was implemented, we took the multigetattr function originally used for SpreadsheetExportMixin (list_export & co.). This function follows the same logic as Django templates' variable resolution logic: https://github.com/wagtail/wagtail/blob/b0b582ab5067e9c649ce0507ff00cb601f81f3ed/wagtail/coreutils.py#L347-L356
  • This means the dot notation is more "powerful"/generic in the sense that it can traverse beyond model relations, e.g. it can be used to access properties of the related instance, or even nested dictionary keys.
  • The list_display attribute is specific to the generic IndexView that works with Django models. We took the high-level API from wagtail-modeladmin (and in turn Django's ModelAdmin), and made it an abstraction for Column that automatically generates the label, sort_key, etc. from the model field. Support for related objects was added recently in #11588, using the double-underscore notation to follow the same feature also recently implemented by Django's ModelAdmin. (Somewhat related: this also ties nicely with list_filter that is passed along to django-filter, which supports the same double-underscore notation.)

My concern is that if we allow the double underscore notation for Column, would that also shift the expectation of behaviour, and whether we need to address it. Some design questions off the top of my head:

  • Are we okay for the Column class to handle the __ syntax, which is typically used for working with Django models?
  • Do we simply convert __ into . and keep the rest of the logic, or do something more to ensure that we're traversing model relationships? If the former, then __ now also works for accessing object properties, which isn't really applicable in other places.
  • Does this mean . is interchangeable with __, i.e. can I use . to span relationships in list_display?

laymonage avatar Jul 01 '25 13:07 laymonage

Just to add to this. This does not only apply to situations where you override using Column. It is also a matter of inconsistency when using list_display and list_export. That is, it is not possible to traverse relationships in list_exports using the __ notation.

class MyAdmin(SnippetViewSet):
    list_display = ('application__user',)
    list_export = ('application.user',)

saevarom avatar Sep 12 '25 09:09 saevarom