Opened 9 months ago

Closed 9 months ago

#35227 closed Bug (invalid)

admin.display(ordering="") is raises exception while trying to ordering field.

Reported by: yetem Owned by: nobody
Component: contrib.admin Version: 5.0
Severity: Normal Keywords:
Cc: yetem Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I have two models.

class A(models.Model):
    pass


class B(models.Model):
    is_success = models.BooleanField(null=True)
    a = models.ForeignKey(A, on_delete=models.CASCADE)

And have defined admin for A class, with field is_success from B class, in that way:

class AAdmin(admin.ModelAdmin):
    list_display = ("is_success", )


    @admin.display(boolean=True, ordering="is_success")
    def is_success(self, obj):
        newest_b = obj.b_set.first()
        if newest_b:
            return newest_b.is_success
        else:
            return

When I tried to order by column is_success, Django admin raises an exception:

FieldError at /admin/test_bug_ordering/a/
Cannot resolve keyword 'is_success' into field. Choices are: b, id
Request Method:	GET
Request URL:	http://localhost:8000/admin/test_bug_ordering/a/?o=1
Django Version:	5.0.2
Exception Type:	FieldError
Exception Value:	
Cannot resolve keyword 'is_success' into field. Choices are: b, id
Exception Location:	/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/db/models/sql/query.py, line 1772, in names_to_path
Raised during:	django.contrib.admin.options.changelist_view
Python Executable:	/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/bin/python
Python Version:	3.11.7
Python Path:	
['/Users/mdabrowski/workspace/test_bug_ordering',
 '/opt/homebrew/Cellar/python@3.11/3.11.7/Frameworks/Python.framework/Versions/3.11/lib/python311.zip',
 '/opt/homebrew/Cellar/python@3.11/3.11.7/Frameworks/Python.framework/Versions/3.11/lib/python3.11',
 '/opt/homebrew/Cellar/python@3.11/3.11.7/Frameworks/Python.framework/Versions/3.11/lib/python3.11/lib-dynload',
 '/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages',
 '/Users/mdabrowski/workspace/aion']
Server time:	Sat, 17 Feb 2024 21:20:27 +0000
Traceback Switch to copy-and-paste view
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/core/handlers/exception.py, line 55, in inner
                response = get_response(request)
                               ^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/core/handlers/base.py, line 197, in _get_response
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/contrib/admin/options.py, line 715, in wrapper
                return self.admin_site.admin_view(view)(*args, **kwargs)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/utils/decorators.py, line 188, in _view_wrapper
                        result = _process_exception(request, e)
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/utils/decorators.py, line 186, in _view_wrapper
                        response = view_func(request, *args, **kwargs)
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/views/decorators/cache.py, line 80, in _view_wrapper
            response = view_func(request, *args, **kwargs)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/contrib/admin/sites.py, line 240, in inner
            return view(request, *args, **kwargs)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/utils/decorators.py, line 48, in _wrapper
        return bound_method(*args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/utils/decorators.py, line 188, in _view_wrapper
                        result = _process_exception(request, e)
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/utils/decorators.py, line 186, in _view_wrapper
                        response = view_func(request, *args, **kwargs)
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/contrib/admin/options.py, line 1984, in changelist_view
            cl = self.get_changelist_instance(request)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/contrib/admin/options.py, line 863, in get_changelist_instance
        return ChangeList(
                     …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/contrib/admin/views/main.py, line 144, in __init__
        self.queryset = self.get_queryset(request)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/contrib/admin/views/main.py, line 574, in get_queryset
        qs = qs.order_by(*ordering)
                  ^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/db/models/query.py, line 1701, in order_by
        obj.query.add_ordering(*field_names)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/db/models/sql/query.py, line 2253, in add_ordering
                self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ …
Local vars
/Users/mdabrowski/Library/Caches/pypoetry/virtualenvs/aion-ATQLib67-py3.11/lib/python3.11/site-packages/django/db/models/sql/query.py, line 1772, in names_to_path
                    raise FieldError(
                          ^ …
Local vars

However, I discovered a strange behavior. I can add a SimpleFilter like this:

class IsSuccessFilter(admin.SimpleListFilter):
    title = "Is Success Filter"
    parameter_name = "is_success"

    def lookups(self, request, model_admin):
        return [
            ("true", _("Yes")),
            ("false", _("No")),
            ("none", _("None")),
        ]

    def queryset(self, request, queryset):
        subquery = (
            B.objects.filter(a_id=OuterRef("id"))
            .values("is_success")[:1]
        )
        queryset = queryset.annotate(is_success=Subquery(subquery))
        if self.value() == "true":
            return queryset.filter(is_success=True)
        elif self.value() == "false":
            return queryset.filter(is_success=False)
        elif self.value() == "none":
            return queryset.exclude(is_success__isnull=True)
        else:
            return queryset


class AAdmin(admin.ModelAdmin):
    list_display = ("is_success", )
    list_filter = (IsSuccessFilter, )


    @admin.display(boolean=True, ordering="is_success")
    def is_success(self, obj):
        newest_b = obj.b_set.first()
        if newest_b:
            return newest_b.is_success
        else:
            return

And now I can order by is_success until I turn on facets option. When I click Show counts the exception occurs again, but without Show counts ordering works correctly.

Change History (1)

comment:1 by Mariusz Felisiak, 9 months ago

Resolution: invalid
Status: newclosed

When I tried to order by column is_success, Django admin raises an exception:

This is an expected behavior, you cannot set ordering by nonexistent fields.

However, I discovered a strange behavior. I can add a SimpleFilter like this:

You added is_success annotation to the queryset so now you can order by it.

And now I can order by is_success until I turn on facets option. When I click Show counts the exception occurs again, but without Show counts ordering works correctly.

This is probably a duplicate of #35198.

Marking as "invalid" as it seems that you're experimenting with Django admin. If you're having trouble understanding how Django works, see TicketClosingReasons/UseSupportChannels for ways to get help.

Note: See TracTickets for help on using tickets.
Back to Top