From 6d5709ce7dc37999a4d12c3ecf2a661afe097b2a Mon Sep 17 00:00:00 2001
From: Keryn Knight <keryn@kerynknight.com>
Date: Mon, 2 Aug 2021 12:05:13 +0100
Subject: [PATCH] Refs #27624 -- Optimized sql.Query creation by moving
 immutable/singleton attributes to class attributes.

---
 django/db/models/sql/query.py | 121 +++++++++++++++++-----------------
 1 file changed, 62 insertions(+), 59 deletions(-)

diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 2b1f6757ba..1faf98d45d 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -155,6 +155,66 @@ class Query(BaseExpression):
     base_table_class = BaseTable
     join_class = Join
 
+    default_cols = True
+    default_ordering = True
+    standard_ordering = True
+
+    filter_is_sticky = False
+    subquery = False
+
+    # SQL-related attributes.
+    # Select and related select clauses are expressions to use in the SELECT
+    # clause of the query. The select is used for cases where we want to set up
+    # the select clause to contain other than default fields (values(),
+    # subqueries...). Note that annotations go to annotations dictionary.
+    select = ()
+    # The group_by attribute can have one of the following forms:
+    #  - None: no group by at all in the query
+    #  - A tuple of expressions: group by (at least) those expressions.
+    #    String refs are also allowed for now.
+    #  - True: group by all select fields of the model
+    # See compiler.get_group_by() for details.
+    group_by = None
+    order_by = ()
+    low_mark = 0  # Used for offset/limit.
+    high_mark = None  # Used for offset/limit.
+    distinct = False
+    distinct_fields = ()
+    select_for_update = False
+    select_for_update_nowait = False
+    select_for_update_skip_locked = False
+    select_for_update_of = ()
+    select_for_no_key_update = False
+    select_related = False
+    # Arbitrary limit for select_related to prevents infinite recursion.
+    max_depth = 5
+    # Holds the selects defined by a call to values() or values_list()
+    # excluding annotation_select and extra_select.
+    values_select = ()
+
+    # SQL annotation-related attributes.
+    annotation_select_mask = None
+    _annotation_select_cache = None
+
+    # Set combination attributes.
+    combinator = None
+    combinator_all = False
+    combined_queries = ()
+
+    # These are for extensions. The contents are more or less appended verbatim
+    # to the appropriate clause.
+    extra_select_mask = None
+    _extra_select_cache = None
+
+    extra_tables = ()
+    extra_order_by = ()
+
+    # A tuple that is a set of model field names and either True, if these are
+    # the fields to defer, or False if these are the only fields to load.
+    deferred_loading = (frozenset(), True)
+
+    explain_info = None
+
     def __init__(self, model, alias_cols=True):
         self.model = model
         self.alias_refcount = {}
@@ -172,74 +232,17 @@ class Query(BaseExpression):
         # Map external tables to whether they are aliased.
         self.external_aliases = {}
         self.table_map = {}  # Maps table names to list of aliases.
-        self.default_cols = True
-        self.default_ordering = True
-        self.standard_ordering = True
         self.used_aliases = set()
-        self.filter_is_sticky = False
-        self.subquery = False
 
-        # SQL-related attributes
-        # Select and related select clauses are expressions to use in the
-        # SELECT clause of the query.
-        # The select is used for cases where we want to set up the select
-        # clause to contain other than default fields (values(), subqueries...)
-        # Note that annotations go to annotations dictionary.
-        self.select = ()
         self.where = WhereNode()
-        # The group_by attribute can have one of the following forms:
-        #  - None: no group by at all in the query
-        #  - A tuple of expressions: group by (at least) those expressions.
-        #    String refs are also allowed for now.
-        #  - True: group by all select fields of the model
-        # See compiler.get_group_by() for details.
-        self.group_by = None
-        self.order_by = ()
-        self.low_mark, self.high_mark = 0, None  # Used for offset/limit
-        self.distinct = False
-        self.distinct_fields = ()
-        self.select_for_update = False
-        self.select_for_update_nowait = False
-        self.select_for_update_skip_locked = False
-        self.select_for_update_of = ()
-        self.select_for_no_key_update = False
-
-        self.select_related = False
-        # Arbitrary limit for select_related to prevents infinite recursion.
-        self.max_depth = 5
-
-        # Holds the selects defined by a call to values() or values_list()
-        # excluding annotation_select and extra_select.
-        self.values_select = ()
-
-        # SQL annotation-related attributes
-        self.annotations = {}  # Maps alias -> Annotation Expression
-        self.annotation_select_mask = None
-        self._annotation_select_cache = None
-
-        # Set combination attributes
-        self.combinator = None
-        self.combinator_all = False
-        self.combined_queries = ()
-
+        # Maps alias -> Annotation Expression.
+        self.annotations = {}
         # These are for extensions. The contents are more or less appended
         # verbatim to the appropriate clause.
         self.extra = {}  # Maps col_alias -> (col_sql, params).
-        self.extra_select_mask = None
-        self._extra_select_cache = None
-
-        self.extra_tables = ()
-        self.extra_order_by = ()
-
-        # A tuple that is a set of model field names and either True, if these
-        # are the fields to defer, or False if these are the only fields to
-        # load.
-        self.deferred_loading = (frozenset(), True)
 
         self._filtered_relations = {}
 
-        self.explain_info = None
-
     @property
     def output_field(self):
         if len(self.select) == 1: