Ticket #23533: 0001-Fixed-23533-Allow-defining-initial-filters-on-QueryS.patch

File 0001-Fixed-23533-Allow-defining-initial-filters-on-QueryS.patch, 3.8 KB (added by Mariusz Felisiak, 3 weeks ago)
  • django/db/models/manager.py

    From e22399d1bf31bcd908a0bbb07dc1384c22a172c1 Mon Sep 17 00:00:00 2001
    From: Mariusz Felisiak <felisiak.mariusz@gmail.com>
    Date: Thu, 27 Nov 2025 10:59:39 +0100
    Subject: [PATCH] Fixed #23533 -- Allow defining initial filters on QuerySet
     classes.
    
    Thanks Simon Charette for the implementation idea.
    ---
     django/db/models/manager.py |  6 ++++--
     django/db/models/query.py   | 24 ++++++++++++++++++++++--
     2 files changed, 26 insertions(+), 4 deletions(-)
    
    diff --git a/django/db/models/manager.py b/django/db/models/manager.py
    index 467e79f9b9..b8648d76cd 100644
    a b  
    11import copy
    22import inspect
    3 from functools import wraps
     3import types
     4from functools import partial, wraps
    45from importlib import import_module
    56
    67from django.db import router
    class BaseManager:  
    9091
    9192        new_methods = {}
    9293        for name, method in inspect.getmembers(
    93             queryset_class, predicate=inspect.isfunction
     94            queryset_class,
     95            predicate=lambda member: isinstance(member, (types.FunctionType, partial)),
    9496        ):
    9597            # Only copy missing methods.
    9698            if hasattr(cls, name):
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index 73d6717bce..089aa5d914 100644
    a b from django.db.models.deletion import Collector  
    2929from django.db.models.expressions import Case, DatabaseDefault, F, Value, When
    3030from django.db.models.fetch_modes import FETCH_ONE
    3131from django.db.models.functions import Cast, Trunc
    32 from django.db.models.query_utils import FilteredRelation, Q
     32from django.db.models.query_utils import FilteredRelation, Q, class_or_instance_method
    3333from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, ROW_COUNT
    3434from django.db.models.utils import (
    3535    AltersData,
    class FlatValuesListIterable(BaseIterable):  
    300300class QuerySet(AltersData):
    301301    """Represent a lazy database lookup for a set of objects."""
    302302
     303    _initial_filter = None
     304
    303305    def __init__(self, model=None, query=None, using=None, hints=None):
    304306        self.model = model
    305307        self._db = using
    class QuerySet(AltersData):  
    323325            negate, args, kwargs = self._deferred_filter
    324326            self._filter_or_exclude_inplace(negate, args, kwargs)
    325327            self._deferred_filter = None
     328        if self._initial_filter is not None:
     329            self._query.add_q(self._initial_filter)
    326330        return self._query
    327331
    328332    @query.setter
    class QuerySet(AltersData):  
    16181622        """
    16191623        return self._chain()
    16201624
    1621     def filter(self, *args, **kwargs):
     1625    def _class_filter(cls, *args, **kwargs):
     1626        if invalid_kwargs := PROHIBITED_FILTER_KWARGS.intersection(kwargs):
     1627            invalid_kwargs_str = ", ".join(f"'{k}'" for k in sorted(invalid_kwargs))
     1628            raise TypeError(f"The following kwargs are invalid: {invalid_kwargs_str}")
     1629        initial_filter = Q(*args, **kwargs)
     1630        initial_filter_id = id(initial_filter)
     1631        class_name = f"{cls.__name__}WithFilter{initial_filter_id}"
     1632        return type(
     1633            class_name,
     1634            (cls,),
     1635            {"_initial_filter": initial_filter},
     1636        )
     1637
     1638    def _instance_filter(self, *args, **kwargs):
    16221639        """
    16231640        Return a new QuerySet instance with the args ANDed to the existing
    16241641        set.
    class QuerySet(AltersData):  
    16261643        self._not_support_combined_queries("filter")
    16271644        return self._filter_or_exclude(False, args, kwargs)
    16281645
     1646    filter = class_or_instance_method(_class_filter, _instance_filter)
     1647    _class_filter = classmethod(_class_filter)
     1648
    16291649    def exclude(self, *args, **kwargs):
    16301650        """
    16311651        Return a new QuerySet instance with NOT (args) ANDed to the existing
Back to Top