N+1 запросов в SerializerMethodField
У меня это view
def get_queryset(self) -> QuerySet[Good]:
....
qs = (
Good.objects.values('brand_id', 'brand__name')
.annotate(
....
)
.prefetch_related(Prefetch('history', StocksHistory.objects.filter(Q(**subquery_filter_args))))
.order_by('-total_sales')
)
return qs
и сериализатор
class ExtendedBrandSerializer(serializers.ModelSerializer):
...
history = serializers.SerializerMethodField()
class Meta:
model = Good
fields = (
...
'history',
)
def get_history(self, good: dict) -> dict:
....
return StocksHistorySerializer(
StocksHistory.objects.extra(select={'day': 'date( snap_at )'})
.values('day')
.filter(history_filter_query)
.annotate(
....
),
many=True,
).data
Отношение: StocksHistory (*) -> (1) Good
.
У меня есть N+1
запросов в SerializerMethodField
. Как я могу это исправить?
Возможно, есть способ перенести аннотацию из сериализатора в представление?
В итоге мне также нужен ключ history в ответе, который будет содержать список этих дочерних объектов.
Вы можете переместить все ваши фильтры и аннотации на метод get_queryset
:
def get_queryset(self) -> QuerySet[Good]:
# ...
qs = (
Good.objects.values(
'brand_id', 'brand__name'
).annotate(
# ...
).prefetch_related(
Prefetch(
'history',
StocksHistory.objects.filter(
Q(**subquery_filter_args)
).extra(
select={'day': 'date( snap_at )'}
).annotate(
# ...
)
)
).order_by('-total_sales')
)
return qs
Тогда просто используйте good.history.all()
в вашем методе сериализатора, чтобы избежать N+1, вот так:
class ExtendedBrandSerializer(serializers.ModelSerializer):
# ...
history = serializers.SerializerMethodField()
class Meta:
model = Good
fields = (
# ...
'history',
)
def get_history(self, good: dict) -> dict:
# ...
return StocksHistorySerializer(
good.history.all(),
many=True,
).data