﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
34776	Issue with maintaining condition order and join promotion in add_q method	Songhee Han	nobody	"- related ticket : https://code.djangoproject.com/ticket/29125
 
- The `__init__ `method class Q sorts sorted, while add_q method class Query does

- The issue stemmed from Python 3.5's non-deterministic dictionary ordering, which resulted in the absence of sorted kwargs. Consequently, instances of the Q object having multiple kwargs led to unpredictable outcomes in the deconstruct() method. Recognizing this problem, the incorporated changes were identified to be causing disparate outcomes between class Q and class Query.

django.db.models.query_utils.py
{{{
class Q(tree.Node):
    """"""
    Encapsulate filters as objects that can then be combined logically (using
    `&` and `|`).
    """"""

    # Connection types
    AND = ""AND""
    OR = ""OR""
    XOR = ""XOR""
    default = AND
    conditional = True

    def __init__(self, *args, _connector=None, _negated=False, **kwargs):
        super().__init__(
            children=[*args, *sorted(kwargs.items())],
            connector=_connector,
            negated=_negated,
        )
}}}

django.db.models.sql.query.py


{{{
class Query(BaseExpression):
 ...
    def add_q(self, q_object):
        """"""
        A preprocessor for the internal _add_q(). Responsible for doing final
        join promotion.
        """"""
        # For join promotion this case is doing an AND for the added q_object
        # and existing conditions. So, any existing inner join forces the join
        # type to remain inner. Existing outer joins can however be demoted.
        # (Consider case where rel_a is LOUTER and rel_a__col=1 is added - if
        # rel_a doesn't produce any rows, then the whole condition must fail.
        # So, demotion is OK.
        existing_inner = {
            a for a in self.alias_map if self.alias_map[a].join_type == INNER
        }
        clause, _ = self._add_q(q_object, self.used_aliases)
        if clause:
            self.where.add(clause, AND)
        self.demote_joins(existing_inner)

}}}


Ensuring that conditions are controlled and combined in a consistent order is crucial for maintaining the coherence of join promotion and ensuring the accuracy of the overall query behavior. This is precisely why handling the order of conditions appropriately within the add_q method is pivotal for avoiding unintended changes to join types and guaranteeing accurate query results.
"	Cleanup/optimization	closed	Database layer (models, ORM)	4.0	Normal	invalid			Unreviewed	0	0	0	0	1	0
