Skip to content

Support filtering on related fields#83

Open
gasman wants to merge 5 commits intowagtail:mainfrom
gasman:feature/filter-by-related-fields-2
Open

Support filtering on related fields#83
gasman wants to merge 5 commits intowagtail:mainfrom
gasman:feature/filter-by-related-fields-2

Conversation

@gasman
Copy link
Copy Markdown
Contributor

@gasman gasman commented Mar 9, 2026

Fixes #12

Fix the validation phase of BaseSearchQueryCompiler._process_filter so that it correctly descends into RelatedFields definitions when confirming that a field being filtered on has a corresponding FilterField. (Previously it only looked in the search_fields definition of the base model of the query, giving spurious "Please add index.FilterField" errors.) This is sufficient to make filtering by related fields work for the database search backends (because these work by annotating and filtering the original query, retaining all existing filtering logic).

For Elasticsearch-based engines, we also need to rewrite the filter clause into Elasticsearch query syntax: where a filter on a field of the base model looks like:

{"term": {"number_of_pages_filter": 320}}

the equivalent for a field on a related model is:

{"nested": {
  "path": "authors",
  "query": {"term": {"authors.date_of_birth_filter": "1892-01-03"}}
}}

To achieve this, we revise the API of _process_lookup (which is only implemented by the Elasticsearch backends) so that it receives the full chain of RelatedFields definitions leading up to the FilterField being filtered on, and accepts a mapping parameter so that it can work with the mapping of any model (rather than just self.mapping which is tied to the query's root model). We can then call _process_lookup recursively to build the inner {"term": ...} clause, and wrap it in the {"nested": ...} clause.

gasman added 5 commits March 7, 2026 03:36
Previously, when validating that fields in a `filter` clause had a corresponding FilterField, `_process_filter` simply took the attname (e.g. `title` for `Character.objects.filter(novel__title="The Hobbit")`) and looked for it in the queryset model's `search_fields`, not accounting for the fact that it's a field from a different model. Fix this by introducing a new method `_get_filter_field_for_column` that considers the Col object of the filter in full, and descends into `RelatedFields` as needed.

Fixing this is sufficient to make these lookups work on database backends; for Elasticsearch we still need to deal with translating this lookup into Elasticsearch query structure.
Rename `_get_filter_field_for_column` to `_get_filter_field_path_for_column`, make it track (and return) the full sequence of search fields rather than just the final entry, and make it responsible for raising FilterFieldError messages rather than returning None on not found. This allows error messages to be more specific about where the missing field is, rather than just giving the base model and final field name.
@gasman gasman requested a review from kaedroho March 9, 2026 23:58
@gasman gasman self-assigned this Mar 9, 2026
@gasman gasman added the enhancement New feature or request label Mar 9, 2026
@gasman gasman moved this to 👀 In review in Wagtail 7.4 LTS release planning Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

Filtering on index.RelatedFields

1 participant