Почему Django prefetch_related приводит к сотням лишних SQL-запросов?
Мои отношения таковы:
- Портфель имеет отношение многие 2 многие к LoanTape через PortfolioLoanTapes .
- У котировки есть много LoanTapes
- У котировки много документов
Я пытаюсь запросить Portfolio
и все связанные с ним документы со звездами в вызове API GET портфеля в DRF ModelViewSet.
models.py
class Portfolio(models.Model):
loan_tapes = models.ManyToManyField("LoanTape", through="PortfolioLoanTapes")
class PortfolioLoanTapes(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.PROTECT)
loan_tape = models.ForeignKey(LoanTape, on_delete=models.PROTECT)
class LoanTape(models.Model):
portfolios = models.ManyToManyField("Portfolio", through="PortfolioLoanTapes")
quote = models.ForeignKey("Quote", on_delete=models.PROTECT, blank=False, null=False, related_name="loan_tapes")
class Quote(models.Model):
...
class Document(models.Model):
quote = models.ForeignKey(Quote, on_delete=models.PROTECT, related_name="documents")
is_starred = models.BooleanField(default=False)
view.py
def get_queryset(self):
starred_documents_qs = Document.objects.filter(is_starred=True)
quotes_prefetch = Prefetch('quote__documents', queryset=starred_documents_qs, to_attr='starred_documents')
loan_tapes_prefetch = Prefetch('portfolioloantapes_set__loan_tape', queryset=LoanTape.objects.prefetch_related(quotes_prefetch))
qs = Portfolio.objects.prefetch_related(loan_tapes_prefetch).filter(deleted__isnull=True)
return qs
serializer.py
class PortfolioDetailSerializer(serializers.ModelSerializer):
loan_tapes = PortfolioLoanTapeSerializer(many=True, read_only=True, source="portfolioloantapes_set")
class PortfolioLoanTapeSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
ret.update(LoanTapeSerializer(instance.loan_tape).data)
return ret
class LoanTapeSerializer(serializers.ModelSerializer):
image_url = serializers.SerializerMethodField()
def get_image_url(self, instance):
if starred_docs := getattr(instance.quote, 'starred_documents', []):
return starred_docs[0].url
test.py (не хватает некоторых объектов настройки)
def test_retrieve_query_count(self):
with self.assertNumQueries(4):
response = self.client.get(f"/api/v1/portfolios/{portfolio.id}/", **self.headers)
self.assertEqual(response.status_code, 200)
При утверждении количества запросов в тесте выше, Django делает 224 вызова DB!!! Может быть, мои объекты prefetch настроены неправильно? Есть ли более эффективный путь в префетче? Я совсем запутался и не могу понять, почему так много SQL-запросов делается на такой относительно простой вызов.....