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
Prev Previous commit
Next Next commit
Refs #36385 -- Removed QuerySet._defer_next_filter.
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.
  • Loading branch information
ngnpope committed May 12, 2025
commit 46a4c47eddb6ff58638e7cd9920bd892e0aa1b2f
8 changes: 4 additions & 4 deletions django/db/models/fields/related_descriptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,8 +687,7 @@ def _apply_rel_filters(self, queryset):
queryset._add_hints(instance=self.instance)
if self._db:
queryset = queryset.using(self._db)
queryset._defer_next_filter = True
queryset = queryset.filter(**self.core_filters)
queryset = queryset._filter_q(Q(**self.core_filters), defer=True)
for field in self.field.foreign_related_fields:
val = getattr(self.instance, field.attname)
if val is None or (val == "" and empty_strings_as_null):
Expand Down Expand Up @@ -1088,8 +1087,9 @@ def _apply_rel_filters(self, queryset):
queryset._add_hints(instance=self.instance)
if self._db:
queryset = queryset.using(self._db)
queryset._defer_next_filter = True
return queryset._next_is_sticky().filter(**self.core_filters)
return queryset._next_is_sticky()._filter_q(
Q(**self.core_filters), defer=True
)

def get_prefetch_cache(self):
try:
Expand Down
6 changes: 2 additions & 4 deletions django/db/models/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ def __init__(self, model=None, query=None, using=None, hints=None):
self._known_related_objects = {} # {rel_field: {pk: rel_obj}}
self._iterable_class = ModelIterable
self._fields = None
self._defer_next_filter = False
self._deferred_filter = None

@property
Expand Down Expand Up @@ -1495,12 +1494,11 @@ def exclude(self, *args, **kwargs):
self._not_support_combined_queries("exclude")
return self._filter_q(~Q(*args, **kwargs))

def _filter_q(self, q):
def _filter_q(self, q, *, defer=False):
if q and self.query.is_sliced:
raise TypeError("Cannot filter a query once a slice has been taken.")
clone = self._chain()
if self._defer_next_filter:
self._defer_next_filter = False
if defer:
clone._deferred_filter = q
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be some logic relying on this attribute being tuple[bool, tuple, dict] and not Q.

else:
clone.query.add_q(q)
Expand Down
3 changes: 1 addition & 2 deletions tests/queryset_pickle/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,7 @@ def test_annotation_values_list(self):

def test_filter_deferred(self):
qs = Happening.objects.all()
qs._defer_next_filter = True
qs = qs.filter(id=0)
qs = qs._filter_q(models.Q(id=0), defer=True)
self.assert_pickles(qs)

def test_missing_django_version_unpickling(self):
Expand Down
0