Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #10743 -- Allowed lookups for related fields in ModelAdmin.list_display. #17357

Merged
merged 2 commits into from Feb 6, 2024

Conversation

knyghty
Copy link
Member

@knyghty knyghty commented Oct 12, 2023

Following up on #16726

I rebased the PR, changed the wording slightly (still not very sure about it, it's quite a long sentence), used a sentinel, and added a test for when the check fails, which I think addresses everything in the original PR.

@nessita nessita self-requested a review October 12, 2023 20:00
Copy link
Contributor

@nessita nessita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Thank you for picking this up. Overall looks good, but we should support the lookup in related models even if they are null=True (see error and failing test).

django/contrib/admin/utils.py Outdated Show resolved Hide resolved
docs/releases/5.1.txt Outdated Show resolved Hide resolved
@nessita
Copy link
Contributor

nessita commented Oct 25, 2023

I continued my testing with the latest code, and don't ask me how I ran into this (because I stumbled upon this by chance) but when using the o query string that defines ordering, exceptions occurs if the referenced column is obtained from using a lookup in a related field.

It took me a while to understand what was going on, but this test shows the issue:

diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py
index bb73cda87d..13985a02e3 100644
--- a/tests/admin_changelist/tests.py
+++ b/tests/admin_changelist/tests.py
@@ -1642,6 +1642,22 @@ class ChangeListTests(TestCase):
         self.assertContains(response, '<td class="field-parent__name">-</td>')
         self.assertContains(response, '<td class="field-parent__parent__name">-</td>')
 
+    def test_list_display_foreign_field_ordering(self):
+        request = self.factory.get("/grandchild/?o=2")
+        request.user = self.superuser
+
+        class GrandChildAdmin(admin.ModelAdmin):
+            list_display = ["name", "parent__name", "parent__parent__name"]
+
+        m = GrandChildAdmin(GrandChild, custom_site)
+        response = m.changelist_view(request)
+        # we should have a decent assert here!
 
 class GetAdminLogTests(TestCase):
     def test_custom_user_pk_not_named_id(self):

As you can see, this is independent from having any actual entry in the list. The traceback is:

======================================================================
ERROR: test_list_display_foreign_field_ordering (admin_changelist.tests.ChangeListTests.test_list_display_foreign_field_ordering)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/nessita/fellowship/django/django/db/models/options.py", line 670, in get_field
    return self.fields_map[field_name]
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 'parent__name'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/nessita/fellowship/django/django/contrib/admin/views/main.py", line 364, in get_ordering_field
    field = self.lookup_opts.get_field(field_name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/db/models/options.py", line 672, in get_field
    raise FieldDoesNotExist(
django.core.exceptions.FieldDoesNotExist: GrandChild has no field named 'parent__name'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/nessita/fellowship/django/tests/admin_changelist/tests.py", line 1657, in test_list_display_foreign_field_ordering
    response = m.changelist_view(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/utils/decorators.py", line 48, in _wrapper
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/utils/decorators.py", line 188, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/utils/decorators.py", line 186, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/contrib/admin/options.py", line 1962, in changelist_view
    cl = self.get_changelist_instance(request)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/contrib/admin/options.py", line 862, in get_changelist_instance
    return ChangeList(
           ^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/contrib/admin/views/main.py", line 144, in __init__
    self.queryset = self.get_queryset(request)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/contrib/admin/views/main.py", line 573, in get_queryset
    ordering = self.get_ordering(request, qs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/contrib/admin/views/main.py", line 400, in get_ordering
    order_field = self.get_ordering_field(field_name)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/nessita/fellowship/django/django/contrib/admin/views/main.py", line 374, in get_ordering_field
    attr = getattr(self.model, field_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'GrandChild' has no attribute 'parent__name'

I think this needs to be fixed, at the very least it should not raise an exception. But, this also raises the question of ordering for these newly allowed columns. I think ordering should be supported, specially because the docs says:

The ordering argument supports query lookups to sort by values on related models. [...]

@knyghty What do you think?

@knyghty
Copy link
Member Author

knyghty commented Oct 26, 2023

@nessita what I think is that this seems hard to do cleanly 🤔 - or am I missing something.

I have pushed a very hacky solution, so hacky I didn't bother to write tests for it, rather I'd like to get feedback first if you can see a less silly way of doing this?

@nessita
Copy link
Contributor

nessita commented Oct 26, 2023

@nessita what I think is that this seems hard to do cleanly 🤔 - or am I missing something.

I have pushed a very hacky solution, so hacky I didn't bother to write tests for it, rather I'd like to get feedback first if you can see a less silly way of doing this?

I don't have an answer beforehand, I need to "seat for a while" and do some investigating. I'm happy to do so, likely next week. If, by any chance, you'd find a "less hacky" way of doing it before that, please mention me and push it!

@knyghty
Copy link
Member Author

knyghty commented Nov 2, 2023

@nessita I made a marginal improvement and added tests for ordering as we'll need those either way.

Copy link
Contributor

@nessita nessita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The branch is looking great! I added that I would say is a final round of comments after a thorough review. I spent a considerable amount of time testing it, I tried very hard to break it, I got creative but not enough it seems :-)

One thing though, that AFAIK is by design, is that property from related fields are not allowed. Does this match your expectation?

Thank you!

django/contrib/admin/checks.py Show resolved Hide resolved
django/contrib/admin/utils.py Outdated Show resolved Hide resolved
django/contrib/admin/utils.py Show resolved Hide resolved
django/contrib/admin/views/main.py Show resolved Hide resolved
docs/ref/contrib/admin/index.txt Outdated Show resolved Hide resolved
tests/admin_checks/tests.py Outdated Show resolved Hide resolved
tests/admin_changelist/tests.py Outdated Show resolved Hide resolved
tests/admin_changelist/tests.py Outdated Show resolved Hide resolved
tests/admin_changelist/tests.py Outdated Show resolved Hide resolved
tests/admin_changelist/tests.py Outdated Show resolved Hide resolved
@knyghty
Copy link
Member Author

knyghty commented Nov 9, 2023

One thing though, that AFAIK is by design, is that property from related fields are not allowed. Does this match your expectation?

Just IMO, but yes, I think this is consistent with the rest of Django. You can't use properties for filtering, and of course we certainly couldn't use them here for ordering. So I think it's expected. If it's desirable or not is another question but I think we should leave that for another ticket.

@knyghty
Copy link
Member Author

knyghty commented Nov 11, 2023

Thanks for the review @nessita, think I've covered all the bases now, hopefully correctly 😄

@nessita
Copy link
Contributor

nessita commented Jan 8, 2024

Hey @knyghty, I know you are busy because of the moving but wanted to check if this PR should be left opened for a little longer, or if we should try to find someone else that adds the necessary Selenium test for the column ordering. Thank you for your work so far! 💯

@knyghty
Copy link
Member Author

knyghty commented Jan 8, 2024

@nessita please keep this open, I plan to either pair on this with one of my Djangonaut Space cohort, or if nobody is interested, do it myself in the coming weeks.

@nessita
Copy link
Contributor

nessita commented Jan 8, 2024

@nessita please keep this open, I plan to either pair on this with one of my Djangonaut Space cohort, or if nobody is interested, do it myself in the coming weeks.

Thank you, pairing with a Djangonaut sounds like an amazing learning opportunity for them ❤️

Copy link
Contributor

@nessita nessita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very close to merging state!

I just added a few nitpicks and a (soft?) request for a few unit tests for the changes in the utils module.

Let me know your thoughts! Thank you.

AUTHORS Show resolved Hide resolved
django/contrib/admin/checks.py Show resolved Hide resolved
django/contrib/admin/utils.py Show resolved Hide resolved
django/contrib/admin/utils.py Show resolved Hide resolved
docs/ref/contrib/admin/index.txt Outdated Show resolved Hide resolved
docs/releases/5.1.txt Outdated Show resolved Hide resolved
tests/admin_changelist/tests.py Outdated Show resolved Hide resolved
Comment on lines 2159 to 2164
self.assertTrue("Parent C" in result_list_rows[1].text)
self.assertTrue("Grandchild Z" in result_list_rows[1].text)
self.assertTrue("Parent B" in result_list_rows[2].text)
self.assertTrue("Grandchild Y" in result_list_rows[2].text)
self.assertTrue("Parent A" in result_list_rows[3].text)
self.assertTrue("Grandchild X" in result_list_rows[3].text)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not assertIn()?

@nessita nessita force-pushed the list-display-lookups branch 2 times, most recently from 217464d to 846c83d Compare February 5, 2024 18:28
@nessita nessita added the selenium Apply to have Selenium tests run on a PR label Feb 5, 2024
@nessita nessita changed the title Fixed #10743 -- Allowed lookup separators in list_display Fixed #10743 -- Allowed lookups for related fields in ModelAdmin.list_display. Feb 5, 2024
Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left small suggestions.

tests/admin_changelist/tests.py Outdated Show resolved Hide resolved
tests/admin_utils/tests.py Outdated Show resolved Hide resolved
tests/modeladmin/test_checks.py Show resolved Hide resolved
@nessita nessita force-pushed the list-display-lookups branch 2 times, most recently from ca6f95a to a5bc5df Compare February 5, 2024 22:47
Copy link
Contributor

@nessita nessita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @knyghty and @nmenezes0 !

…n.list_display.

Co-authored-by: Alex Garcia <me@alexoteiza.com>
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Co-authored-by: Nina Menezes <https://github.com/nmenezes0>
…display.

Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Co-authored-by: Nina Menezes <https://github.com/nmenezes0>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Djangonauts 🚀 selenium Apply to have Selenium tests run on a PR
Projects
None yet
3 participants