Opened 9 months ago

Closed 9 months ago

#34776 closed Cleanup/optimization (invalid)

Issue with maintaining condition order and join promotion in add_q method

Reported by: Songhee Han Owned by: nobody
Component: Database layer (models, ORM) Version: 4.0
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description


  • 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.

Change History (1)

comment:1 by Natalia Bidart, 9 months ago

Resolution: invalid
Status: newclosed

Hello Songhee Han,

I'm having a hard time understanding this ticket report. Could you please explain in more detail what's the concrete issue you are experiencing? A django sample project that exercises the problem would be ideal.

Also, please note that there is no version of Django supporting Python 3.5, and that Python 3.5 has reached end-of-life 3 years ago.

I'm closing the ticket due to the lack of details. You could also seek further help in the user support channels from this link.

Regards, Natalia.

Note: See TracTickets for help on using tickets.
Back to Top