简体   繁体   English

使用 GenericForeignKey 预取模型

[英]Prefetching model with GenericForeignKey

I have a data structure in which a Document has many Blocks which have exactly one Paragraph or Header .我有一个数据结构,其中Document有许多Blocks ,这些Blocks正好有一个ParagraphHeader 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.) (请注意,与上面的实现不同,实际上需要在单独的模型中使用ParagraphHeader 。)

I use jinja2 to template a Latex file for the document.我使用jinja2为文档模板化一个 Latex 文件。 Templating is slow though as jinja performs a new database query for every Block and Paragraph or Header.尽管 jinja 为每个块和段落或标题执行新的数据库查询,但模板化很慢。

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) content_block.latex_text()是一个将 HTML 字符串转换为 Latex 字符串的函数)

Hence I would like to prefetch script.blocks and blocks.content_block .因此我想预取script.blocksblocks.content_block I understand that there are two methods for prefetching in Django:我知道在Django中有两种预取方法:

  1. select_related() performs a JOIN query but only works on ForeignKeys . select_related()执行JOIN查询,但仅适用于ForeignKeys It would work for script.blocks but not for blocks.content_block .它适用于script.blocks但不适用于blocks.content_block

  2. prefetch_related() works with GenericForeignKeys as well, but if I understand the docs correctly, it can only fetch one ContentType at a time while I have two. prefetch_related()适用于 GenericForeignKeys,但如果我正确理解文档,它一次只能获取一个ContentType ,而我有两个。

Is there any way to perform the necessary prefetching here?有没有办法在这里执行必要的预取? Thank you for your help.感谢您的帮助。

I guess you can prefetch a Generic FK, the problem would arise only if you have another level after generic.我猜你可以预取一个泛型 FK,只有当你在泛型之后有另一个级别时才会出现问题。

Did you try你试过了吗

document = (
 Document.objects
  .select_related('blocks')
  .prefecth_related('blocks__content_block')
)

Does it raise any errors?它会引发任何错误吗? Can you please update your code with the view's code?你能用视图的代码更新你的代码吗?

Not really an elegant solution but you can try using reverse generic relations :不是一个优雅的解决方案,但您可以尝试使用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')

and prefetch on that:并预取:

Document.objects.prefetch_related('block_set__header', 'block_set__paragraph')

then change the template rendering to something like (not tested, will try to test later):然后将模板渲染更改为类似(未测试,稍后将尝试测试):

\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}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM