Reputation: 18680
Following the example on Many To Many relationships for Django: https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/
I want to be able to display in the Django Admin a column on the Article listview which contains the title's of associated Publications. So if I have an article a1
which has publications:
Publication(title='Pub 1')
Publication(title='Pub 2')
I want to see a column in the admin listview showing "Pub 1, Pub 2"
. I could do this with a custom function on the Admin class:
class ArticleAdmin(admin.ModelAdmin):
list_display = ['publication_titles']
def publication_titles(self, obj):
return ', '.join([pub.title for pub in obj.publications.all()])
But that's an N+1 problem: each Article row will execute a query for each associated publication. I see this in the Django debug toolbar, where I see the number of SQL queries executed go from 9 queries to 33 when rendering 24 articles in the listview.
I thought maybe I could do prefetch_related
in the queryset:
class ArticleAdmin(admin.ModelAdmin):
list_display = ['publication_titles']
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset.prefetch_related('publications')
return queryset
def publication_titles(self, obj):
return ', '.join([pub.title for pub in obj.publications.all()])
But that seems to have no effect on the number of SQL queries executed.
I thought I could use annotate()
to annotate the queryset used in the admin, but I'm struggling to figure out how to do that. Like I want to do something like:
class ArticleAdmin(admin.ModelAdmin):
list_display = ['publication_titles']
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset.annotate(publication_titles=<HOW DO I GENERATE THE COLLECTION OF TITLES>)
return queryset
But I'm not grokking what I'd put in place for <HOW DO I GENERATE THE COLLECTION OF TITLES>
.
How can I get the list of Publications displayed without spiking the number of queries to the DB?
Upvotes: 1
Views: 398
Reputation: 476659
QuerySet
s are immutable, this thus means that .prefetch_related
creates a new QuerySet
, you thus work with:
class ArticleAdmin(admin.ModelAdmin):
list_display = ['publication_titles']
def get_queryset(self, request):
return super().get_queryset(request).prefetch_related('publications')
def publication_titles(self, obj):
return ', '.join([pub.title for pub in obj.publications.all()])
Upvotes: 1