Возможно ли использование prefetch_related для извлечения связанных (родительских, дочерних страниц) на страницах Wagtail?

#python #django #wagtail

#python #django #wagtail

Вопрос:

Проблема, с которой я сталкиваюсь, заключается в повторяющемся количестве запросов, что замедляет работу приложения при использовании get_parent () или get_children () в модели страницы. которые также увеличиваются, если на родительской странице есть файл изображения, который используется в шаблоне.

итак, я ищу способ предварительной выборки связанных страниц без создания отношения foriegn key.

допустим, у меня есть модель страницы TVSeries и модель страницы Episode:

 class TvSeries(Page):
    name = models.CharField()
    producer = models.CharField()

    subpage_types = ['Episode']
  
 class Episode(Page):
    title = models.CharField()
    number = models.CharField()
    parent_page_types = ['TvSeries']
  

Необходимо выполнять предварительную выборку TVSeries при запросе модели эпизода! Как уменьшить количество вызовов базы данных? Возможно ли использовать предварительную выборку и выбирать связанные? если да, то как?. и если нет, то каково решение проблемы увеличения количества запросов?

Комментарии:

1. Это только расширяет одну модель Page и не имеет никаких особых различий между TvSeries и Episode ? Можете ли вы показать модель Page

2. Я использую Wagtail CMS. Наследование модели страницы, которая наследуется от Django-treebeard

Ответ №1:

prefetch_related не может использоваться для отношений родительской / дочерней страниц, поскольку они не используют стандартное ForeignKey отношение Django — вместо этого Wagtail (и Treebeard) использует path поле для представления положения в дереве. Это позволяет выполнять запросы, которые невозможно эффективно выполнить с ForeignKey , такие как выборка всех потомков (на любой глубине) страницы.

Следует отметить, что prefetch_related это не «бесплатно» — это сгенерирует один дополнительный запрос для каждого последующего отношения. Методы запроса Treebeard обычно равны или лучше этого по эффективности — например:

 series = TvSeries.objects.get(id=123)
episodes = series.get_children()
  

будет извлекать TvSeries и все его эпизоды в двух запросах, точно так же, как (гипотетическое) prefetch_related выражение:

 # fake code, will not work...
series = TvSeries.objects.filter(id=123).prefetch_related('child_pages')
  

Однако одна из проблем get_children заключается в том, что он будет возвращать только базовые Page экземпляры, поэтому для извлечения определенных полей из Episode требуются дополнительные запросы. Вы можете избежать этого, используя child_of вместо:

 series = TvSeries.objects.get(id=123)
episodes = Episode.objects.child_of(series)
  

Комментарии:

1. Проблема в том, что у вас есть несколько объектов. Допустим, мы хотим получить последние 30 эпизодов с их родителями: latest_episodes = Episode.objects.all()[:30] >>> теперь вам нужно будет выполнить get_parent() или parent_of() для каждого эпизода. это вызов на 30 дб, добавьте к этому, если у родительского файла есть файл изображения, тогда у вас будет вызов еще на 30 дб. Я начинаю думать, что лучшим решением является создание родительского ключа или написание моего собственного необработанного запроса. Спасибо

2. Я тоже борюсь с этим

3. Это правда, что древовидная структура допускает некоторые изящные вещи, но нам гораздо чаще нужен простой родительский поиск… prefetch_related не является «бесплатным», но select_related переводится на стандартные JOIN языки — поскольку каждая страница может иметь только 1 прямого родителя, меня также расстраивает, что дочерняя страница не может получить своего родителя одним запросом. я думаю, мы также добавим идентификатор родительского указателя или что-то подобное…