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]

Back to Top