Search code examples
searchforeign-keysparentchildrenwagtail

How to extend Wagtail search to include child InlinePanel Orderables?


I have a Wagtail site with a page of music conductors. Each conductor has a section with their name, photo and biography. The conductor details are stored inside child Orderable instances which the parent page is displaying using an InlinePanel.

When users search my site, I want the results to contain text coming from inside the conductor details (i.e. based on their name and full biography text in the Orderable).

Conductors Page

    class ConductorsPage(Page):
    max_count = 1
    template = 'home/conductors.html'

    content = RichTextField(
        null=True,
        blank=True,
    )

    search_fields = Page.search_fields + [
        index.SearchField('content'),
        index.SearchField('conductors'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('content', classname="full"),
        MultiFieldPanel(
            [
                InlinePanel("conductors", label="Conductor")
            ],
            heading="Conductors",
        ),
    ]

Conductor (Orderable)`

class Conductor(Orderable):
    page = ParentalKey('home.ConductorsPage', related_name='conductors')

    name = models.CharField(max_length=30)
    photograph = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    biography = RichTextField(
        null=True,
        blank=True,
        features=['bold', 'italic', 'hr', 'link'],
    )

    search_fields = Page.search_fields + [
        index.SearchField('name'),
        index.SearchField('biography'),
    ]

    panels = [
        FieldPanel("name"),
        ImageChooserPanel("photograph"),
        FieldPanel("biography"),
    ]

The above code brings results based on the parent content field but omits text from the Orderable (e.g, biography). I am using a Postgres database (wagtail.contrib.postgres_search.backend).

I've seen suggestions to create a method in the parent that concatenates text from the child fields and set a SearchField on that. But that seems like a clunky solution.

What is the recommended way of implementing this in the latest versions of Wagtail?


Solution

  • Thank you gasman - your comment was the solution I was looking for.

    For anyone who is trying to implement this, Wagtail has a very elegant way of defining search fields for child entities: RelatedFields

    The search fields are added on the parent page using RelatedFields. See code below for an example that worked for me. Now when a user runs a search the results include text within the Conductor's biography and/or name.

    class ConductorsPage(Page):
        max_count = 1
        template = 'home/conductors.html'
    
        content = RichTextField(
            null=True,
            blank=True,
            features=['bold', 'italic', 'hr', 'link', 'image'],
        )
    
        search_fields = Page.search_fields + [
            index.SearchField('content'),
            index.RelatedFields('conductors', [
                index.SearchField('name'),
                index.SearchField('biography'),
            ]),
        ]
    
        content_panels = Page.content_panels + [
            FieldPanel('content', classname="full"),
            FieldPanel('search_description', classname="full"),
            MultiFieldPanel(
                [
                    InlinePanel("conductors", label="Conductor")
                ],
                heading="Conductors",
            ),
        ]
    
    class Conductor(Orderable):
        page = ParentalKey('home.ConductorsPage', related_name='conductors')
    
        name = models.CharField(max_length=30)
        photograph = models.ForeignKey(
            "wagtailimages.Image",
            null=True,
            blank=True,
            on_delete=models.SET_NULL,
            related_name="+",
        )
        biography = RichTextField(
            null=True,
            blank=True,
            features=['bold', 'italic', 'hr', 'link'],
        )
    
        panels = [
            FieldPanel("name"),
            ImageChooserPanel("photograph"),
            FieldPanel("biography"),
        ]