I have a data structure in which a Document has many Blocks which have exactly one Paragraph or Header. A simplified implementation:
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()
(Note that there is an actual need for having Paragraph and Header in separate models unlike in the implementation above.)
I use jinja2 to template a Latex file for the document. Templating is slow though as jinja performs a new database query for every Block and Paragraph or Header.
template = get_template(template_name="latex_templates/document.tex", using="tex")
return template.render(context={'script': self.script})
\documentclass[a4paper,10pt]{report}
\begin{document}
{% for block in chapter.block_set.all() %}
{% if block.content_block_type.name == 'header' %}
\section{ {{- block.content_block.latex_text -}} }
{% elif block.content_block_type.name == 'paragraph' %}
{{ block.content_block.latex_text }}
{% endif %}
{% endfor %}
\end{document}
(content_block.latex_text() is a function that converts a HTML string to a Latex string)
Hence I would like to prefetch script.blocks and blocks.content_block. I understand that there are two methods for prefetching in Django:
select_related()performs aJOINquery but only works onForeignKeys. It would work forscript.blocksbut not forblocks.content_block.prefetch_related()works with GenericForeignKeys as well, but if I understand the docs correctly, it can only fetch oneContentTypeat a time while I have two.
Is there any way to perform the necessary prefetching here? Thank you for your help.
My bad, I did not notice that document is an FK, and reverse FK can not be joined with
select_related.First of all, I would suggest to add
related_name="blocks"anyway.When you prefetch, you can pass the queryset. But you should not pass filters by
doc_id, Django's ORM adds it automatically.And if you pass the queryset, you can also add select/prefetch related call there.
But if you don't need extra filters or annotation, the simpler syntax would probably work for you