Как лучше всего сохранить данные предварительной загрузки, чтобы предотвратить N+1 в NestedModelAdmin
У нас есть проблема N+1, заключающаяся в том, что наша NestedModelAdmin
должна работать с несколькими базами данных.
После того как мы решили обойти эту проблему, мы решили получить ее заранее в get_queryset
. Недостатком является то, что при этом будут получены все записи в этой таблице.
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)
Мы пытались использовать queryset super().get_queryset(request)
для уменьшения количества записей, но это не работает, после проверки некоторых документов выяснилось, что get_queryset
вызывается ДО того, как произойдет пагинация
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]))
Другим решением является имитация пагинации, но это только усложнит код
page = int(request.GET.get('p', 0))
limit = self.list_per_page
subqueryset = queryset[page * limit: (page + 1) * limit]