8000 Fixed #36385 -- Simplified filtering in QuerySet. by ngnpope · Pull Request #19438 · django/django · GitHub
[go: up one dir, main page]

Skip to content

Fixed #36385 -- Simplified filtering in QuerySet. #19438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

ngnpope
Copy link
Member
@ngnpope ngnpope commented Apr 30, 2025

Trac ticket number

ticket-36385

Branch description

Some of the code for filtering QuerySet is overly complex which makes it hard to follow:

  • Methods that are nearly identical, e.g. QuerySet._clone() vs QuerySet._chain() and Query.clone() vs Query.chain()
  • Private API like Queryset.complex_filter() which exists for a specific case but can be handled using public APIs
  • Flags such as _defer_next_filter and _sticky_filter that affect the next call to QuerySet.filter()
  • Passing around deconstructed pieces instead of building a Q() as early as possible
  • Not using the klass argument to Query.chain()

I had hoped to eliminate Query.filter_is_sticky and pass the used_aliases to Query.add_q(), but I think that's a non-starter as it has to apply after the next clone. (Hard to get my head around it!)

Checklist

  • This PR targets the main branch.
  • The commit message is written in past tense, mentions the ticket number, and ends with a period.
  • I have checked the "Has patch" ticket flag in the Trac system.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

@github-actions github-actions bot added the no ticket Based on PR title, no linked Trac ticket label Apr 30, 2025
@ngnpope ngnpope force-pushed the simplify-queryset-filter branch from 37eddbc to e618f01 Compare April 30, 2025 19:39
ngnpope added 8 commits May 12, 2025 13:03
…add_q().

This means the `can_reuse` name for `Q.add_q()` and `Q.build_filter()`
follows through.
This is unnecessary complexity as we can already handle this by manually
constructing a `Q()` object if the value is a `dict`, or we can simply
call `.filter(**limit_choices_to)`. Another undocumented function
instead of using the available public API which is perfectly sufficient
makes this all just a little bit harder to understand.
…xclude().

Instead of having to pass around the deconstructed parts, construct the
`Q()` object as early as possible. This allows removal of the separate
`_filter_or_exclude()` and `_filter_or_exclude_inplace()` functions in
favour of a simple `_filter_q()` which takes a `Q()` object directly.
Added a `defer` argument to `QuerySet._filter_q()` instead.

As we're immediately performing a `.filter()` call, make use of the
underlying internal `._filter_q()` method and pass a flag directly.
…sticky().

Added a `sticky` argument to `QuerySet._filter_q()` instead.

As we're immediately performing a `.filter()` call, make use of the
underlying internal `._filter_q()` method and pass a flag directly. When
the flag is passed, we can simply set `filter_is_sticky` on the query
object after cloning the queryset.
After earlier changes this is only calling `._clone()`, so remove it.
There is no need for these to be different and it just makes the usage
of them inconsistent. It was also harder to understand what the value of
`Query.used_aliases` was because the `.clone()` would set the value to
what would be expected when `filter_is_sticky` was set which is the rare
case and this was then undone in `.chain()` when the flag was not set
(the common case) which would make `used_aliases` an empty set.
We pass `defer=True` in two places and `sticky=True` in one of those
places. That means that in the non-deferred case we don't need to handle
`sticky=True`. And given the deferred filter handling - when `.add_q()`
is called will happen when `.query` is next accessed - and therefore
before `Query.chain()` gets called, we can push down assigning `True` to
`.filter_is_sticky` till then.

Doing this will mean that it also applies to the other place that we
call `._filter_q()` with `defer=True`, but that doesn't seem to be an
issue - all tests passed.

The handling of `.filter_is_sticky` has also been moved to `.clone()`
from `.chain()` which makes it a bit clearer and will allow us to
simplify further.
@ngnpope ngnpope force-pushed the simplify-queryset-filter branch from e618f01 to 9b28654 Compare May 12, 2025 12:04
@ngnpope ngnpope changed the title Refs #????? -- Simplified filtering in QuerySet. Fixed #36385 -- Simplified filtering in QuerySet. May 12, 2025
@github-actions github-actions bot removed the no ticket Based on PR title, no linked Trac ticket label May 12, 2025
@ngnpope ngnpope marked this pull request as ready for review May 12, 2025 12:06
@ngnpope ngnpope changed the title Fixed #36385 -- Simplified filtering in QuerySet. Fixed #36385 -- Simplified filtering in QuerySet. May 12, 2025
@ngnpope ngnpope requested a review from charettes May 12, 2025 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
0