Opened 18 months ago
Last modified 3 weeks ago
#34566 assigned Bug
ModelAdmin get_field_queryset uses related admin ordering, but not related admin querysets. — at Initial Version
Reported by: | Mike J | Owned by: | nobody |
---|---|---|---|
Component: | contrib.admin | Version: | 4.2 |
Severity: | Normal | Keywords: | |
Cc: | fisadev, Ramiro Morales, Krishna2864, Ülgen Sarıkavak | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
The ModelAdmin get_field_queryset function properly finds the related model admin and gets the ordering from that. However, it does not also get the fully annotated queryset which can lead to errors breaking the admin page.
In my case I created my own User admin page and added the user permission count to the queryset as follows:
class UserLocalAdmin(UserAdmin): def custom_perm_count(user): return user.user_permissions__count custom_perm_count.admin_order_field = "user_permissions__count" custom_perm_count.short_description = "Permission Count" def get_queryset(self, request): qs = super().get_queryset(request) return qs.annotate(models.Count("user_permissions")) list_display = ("username", "email", "is_staff", "is_superuser", custom_perm_count, "is_active","last_login") sortable_by = list_display ordering = ("-is_active", "-user_permissions__count", "-is_superuser", "username")
Then in a different model I use user as a foreign key titled owner. I also created an admin page for that model.
The issue is the drop down list created for the "owner" foreign key field fetches the admin page ordering, but the un-altered queryset.
This causes a FieldError to be raised and django fails to load the page.
Currently the only option I can see to handle this would be override the get_field_queryset function and allow it to reference the get_queryset function or provide custom ordering.
class ReportFilterAdmin(dj_admin.ModelAdmin): form=AdminReportFilterForm def get_field_queryset(self, db, db_field, request): """ An override of the original to add the required annotations. If the ModelAdmin specifies ordering, the queryset should respect that ordering. Otherwise don't specify the queryset, let the field decide (return None in that case). """ related_admin = self.admin_site._registry.get(db_field.remote_field.model) if related_admin is not None: ordering = related_admin.get_ordering(request) if ordering is not None and ordering != (): # return db_field.remote_field.model._default_manager.using(db).order_by( # current line in official repo return related_admin.get_queryset(request).order_by( *ordering ) return None
I don't know if there are performance issues with my approach, but I believe it achieves more universally expected behavior.
original code: https://github.com/django/django/blob/7414704e88d73dafbcfbb85f9bc54cb6111439d3/django/contrib/admin/options.py#L253-L255