Wagtail фильтрует дочерние страницы по ForeignKey - PullRequest
1 голос
/ 14 июля 2020

Я использую Wagtail , и я хочу отфильтровать выбор дочерних страниц по внешнему ключу. Я пробовал следующее, и при попытке children = self.get_children().specific().filter(use_case__slug=slug) получаю сообщение об ошибке django.core.exceptions.FieldError: Cannot resolve keyword 'use_case' into field:

class AiLabResourceMixin(models.Model):
  parent_page_types = ['AiLabResourceIndexPage']
  use_case = models.ForeignKey(AiLabUseCase, on_delete=models.PROTECT)
  content_panels = ArticlePage.content_panels + [
    FieldPanel('use_case', widget=forms.Select())
  ]

  class Meta:
    abstract = True

class AiLabCaseStudy(AiLabResourceMixin, ArticlePage):
  pass

class AiLabBlogPost(AiLabResourceMixin, ArticlePage):
  pass

class AiLabExternalLink(AiLabResourceMixin, ArticlePage):
  pass

class AiLabResourceIndexPage(RoutablePageMixin, BasePage):
  parent_page_types = ['AiLabHomePage']
  subpage_types = ['AiLabCaseStudy', 'AiLabBlogPost', 'AiLabExternalLink']
  max_count = 1

  @route(r'^$')
  def all_resources(self, request):
    children = self.get_children().specific()

    return render(request, 'ai_lab/ai_lab_resource_index_page.html', {
      'page': self,
      'children': children,
    })

  @route(r'^([a-z0-9]+(?:-[a-z0-9]+)*)/$')
  def filter_by_use_case(self, request, slug):
    children = self.get_children().specific().filter(use_case__slug=slug)

    return render(request, 'ai_lab/ai_lab_resource_index_page.html', {
      'page': self,
      'children': children,
    })

Я видел этот ответ , но это предполагает, что у меня есть только один тип страницы, которую я хочу отфильтровать. Использование чего-то вроде AiLabCaseStudy.objects.filter(use_case__slug=slug) работает, но это возвращает только AiLabCaseStudy s, а не AiLabBlogPost s или AiLabExternalLink s.

Есть идеи?

Ответы [ 2 ]

2 голосов
/ 14 июля 2020

На уровне базы данных нет эффективного способа запустить фильтр сразу для всех типов страниц. Поскольку AiLabResourceMixin определяется как abstract = True, этот класс не имеет собственного представления в базе данных - вместо этого поле use_case определяется отдельно для каждого из AiLabCaseStudy, AiLabBlogPost и AiLabExternalLink. В результате Django или Wagtail не могут превратить .filter(use_case__slug=slug) в запрос SQL, поскольку use_case относится к трем различным местам в базе данных.

Несколько возможных способов обойти это:

  • Если ваша модель данных позволяет, реструктурируйте ее, чтобы использовать многотабличное наследование - это выглядит довольно похоже на ваше текущее определение, за исключением abstract = True :

    class AiLabResourcePage(ArticlePage):
      use_case = models.ForeignKey(AiLabUseCase, on_delete=models.PROTECT)
    
    class AiLabCaseStudy(AiLabResourcePage):
      pass
    
    class AiLabBlogPost(AiLabResourcePage):
      pass
    
    class AiLabExternalLink(AiLabResourcePage):
      pass
    

    AiLabResourcePage будет тогда существовать отдельно в базе данных, и вы можете запросить его поле use_case с выражением вроде: AiLabResourcePage.objects.child_of(self).filter(use_case__slug=slug).specific(). Здесь будет небольшое влияние на производительность, поскольку Django должен извлекать данные из одной дополнительной таблицы для создания этих объектов страницы.

  • Выполнить предварительный запрос для каждого конкретного c тип страницы для получения совпадающих идентификаторов страниц перед выполнением окончательного запроса с specific():

    case_study_ids = list(AiLabCaseStudy.objects.child_of(self).filter(use_case__slug=slug).values_list('id', flat=True))
    blog_post_ids = list(AiLabBlogPost.objects.child_of(self).filter(use_case__slug=slug).values_list('id', flat=True))
    external_link_ids = list(AiLabExternalLink.objects.child_of(self).filter(use_case__slug=slug).values_list('id', flat=True))
    children = Page.objects.filter(id__in=(case_study_ids + blog_post_ids + external_link_ids)).specific()
    
0 голосов
/ 14 июля 2020

Попробуйте:

children = self.get_children().filter(use_case__slug=slug).specific()

...