About Filtered Autocompletes for Wagtail

If a Wagtail page has a foreign key to a model which you want to have with autocompletion, but the results filtered by some fields, use this technique with a proxy model:

from django.db import models
from django.utils.translation import gettext_lazy as _
from wagtail.admin.panels import FieldPanel
from wagtail.models import Page
from wagtailautocomplete.edit_handlers import AutocompletePanel


class Category(models.Model):
    name = models.CharField(_("Name"), max_length=100)
    slug = models.SlugField(_("Slug"), unique=True)

    class Meta:
        verbose_name = _("Category")
        verbose_name_plural = _("Categories")

    def __str__(self):
        return self.name


class Movie(models.Model):
    title = models.CharField(_("Title"), max_length=255)
    category = models.ForeignKey(
        Category,
        verbose_name=_("Category"),
        on_delete=models.PROTECT,
        related_name="movies",
    )

    class Meta:
        verbose_name = _("Movie")
        verbose_name_plural = _("Movies")

    def __str__(self):
        return self.title

    def autocomplete_label(self):
        return self.title

    @classmethod
    def autocomplete_custom_queryset_filter(cls, search_term):
        return cls.objects.filter(title__icontains=search_term)


class Comedy(Movie):
    # Just for the filtered autocomplete widget

    class Meta:
        proxy = True
        verbose_name = _("Comedy")
        verbose_name_plural = _("Comedies")

    @classmethod
    def autocomplete_custom_queryset_filter(cls, search_term):
        return (
            super()
            .autocomplete_custom_queryset_filter(search_term)
            .filter(category__slug="comedy")
            .distinct()
        )


class FilmFestivalEntry(Page):
    movie = models.ForeignKey(
        # FK still points at the real model, not the proxy
        "movies.Movie",
        verbose_name=_("Comedy"),
        on_delete=models.PROTECT,
        limit_choices_to={"category__slug": "comedy"},
        related_name="comedy_entries",
    )
    screening_notes = models.TextField(_("Screening notes"), blank=True)

    content_panels = Page.content_panels + [
        AutocompletePanel("movie", target_model="movies.Comedy"),
        FieldPanel("screening_notes"),
    ]

Tips and Tricks Programming Django 6.x Django 5.2 Django 4.2 Wagtail 7