Предварительная выборка модели с помощью GenericForeignKey
У меня есть структура данных, в которой Document
имеет множество Blocks
, которые имеют ровно один Paragraph
или Header
. Упрощенная реализация:
class Document(models.Model):
title = models.CharField()
class Block(models.Model):
document = models.ForeignKey(to=Document)
content_block_type = models.ForeignKey(to=ContentType)
content_block_id = models.CharField()
content_block = GenericForeignKey(
ct_field="content_block_type",
fk_field="content_block_id",
)
class Paragraph(models.Model):
text = models.TextField()
class Header(models.Model):
text = models.TextField()
level = models.SmallPositiveIntegerField()
(Обратите внимание, что существует реальная необходимость иметь Paragraph
и Header
в отдельных моделях, в отличие от реализации выше)
Я использую jinja2
для шаблонирования Latex-файла для документа. Шаблонирование происходит медленно, поскольку jinja выполняет новый запрос к базе данных для каждого блока, параграфа или заголовка.
template = get_template(template_name="latex_templates/document.tex", using="tex")
return template.render(context={'script': self.script})
Таким образом, я хотел бы выполнить предварительную выборку script.blocks
и blocks.content_block
. Я понимаю, что в Django есть два метода для предварительной выборки:
select_related()
performs aJOIN
query but only works onForeignKeys
. It would work forscript.blocks
but not forblocks.content_block
.prefetch_related()
works with GenericForeignKeys as well, but if I understand the docs correctly, it can only fetch oneContentType
at a time while I have two.
Есть ли способ выполнить необходимую предварительную выборку здесь? Спасибо за помощь.
Я думаю, что вы можете префетчить Generic FK, проблема возникнет только если у вас есть другой уровень после generic.
Пытались ли вы
document = (
Document.objects
.select_related('blocks')
.prefecth_related('blocks__content_block')
)
Вызывает ли он какие-либо ошибки? Не могли бы вы обновить ваш код с кодом представления?
Не совсем элегантное решение, но вы можете попробовать использовать reverse generic relations
:
from django.contrib.contenttypes.fields import GenericRelation
class Paragraph(models.Model):
text = models.TextField()
blocks = GenericRelation(Block, related_query_name='paragraph')
class Header(models.Model):
text = models.TextField()
level = models.SmallPositiveIntegerField()
blocks = GenericRelation(Block, related_query_name='header')
и префетч на этом:
Document.objects.prefetch_related('block_set__header', 'block_set__paragraph')
затем измените рендеринг шаблона на что-то вроде (не проверено, попробую проверить позже):
\documentclass[a4paper,10pt]{report}
\begin{document}
{% for block in chapter.block_set.all %}
{% if block.header %}
\section{ {{- block.header.0.latex_text -}} }
{% elif block.paragraph %}
{{ block.paragraph.0.latex_text }}
{% endif %}
{% endfor %}
\end{document}