What is the best way to save preload data to prevent N+1 in NestedModelAdmin
We have an N+1 problem that our NestedModelAdmin
has to deal with multiple databases.
After working around it, we decided to fetch it beforehand in get_queryset
. The drawback is that it will fetch all records in that table.
class PODOrderAdmin(nested_admin.NestedModelAdmin):
list_display = ('order_id', 'get_order_name', 'get_order_type', 'get_suppliers')
def get_queryset(self, request):
order_filter = request.GET.get('order')
qs = super().get_queryset(request).prefetch_related(
Prefetch(
'podlineitem_set',
queryset=PODLineItem.objects.select_related('line_item')
),
).select_related('order')
self.suppliers = list(PodSupplier.objects.only('id', 'name'))
return qs
def get_suppliers(self, obj):
# Filter the pod_line_items by pod_order.id to match obj.id
supplier_ids = [line_item.supplier_id for line_item in obj.podlineitem_set.all()]
filtered_suppliers = []
if hasattr(self, 'suppliers'):
filtered_suppliers = [supplier for supplier in self.suppliers if supplier.id in supplier_ids]
else:
filtered_suppliers = PodSupplier.objects.filter(id__in=supplier_ids)
if filtered_suppliers:
supplier_names = [(supplier.name,) for supplier in filtered_suppliers]
return format_html_join('\n', '{}<br>', supplier_names)
return None
get_suppliers.short_description = 'Suppliers'
admin.site.register(PODOrder, PODOrderAdmin)
We tried to use queryset super().get_queryset(request)
to reduce the number of records but it's not working, after checking some documents, it turns out get_queryset
is called BEFORE paging happen
qs = super().get_queryset(request).prefetch_related('podlineitem_set').select_related('order')
pod_order_ids = qs.values_list('id', flat=True)
self.pod_line_items = list(PODLineItem.objects.filter(pod_order_id__in=pod_order_ids))
self.suppliers = list(PodSupplier.objects.filter(id__in=[pod_line_item.supplier_id for pod_line_item in self.pod_line_items]))
Another solution is to mimic paging but it only make code more complicated
page = int(request.GET.get('p', 0))
limit = self.list_per_page
subqueryset = queryset[page * limit: (page + 1) * limit]