From 9d04711261156c487c6085171398070ea3df8122 Mon Sep 17 00:00:00 2001 From: Gagaro Date: Tue, 3 May 2022 14:06:42 +0200 Subject: [PATCH] Refs #30581 -- Added Q.flatten(). --- django/db/models/query_utils.py | 15 +++++++++++++++ tests/queries/test_q.py | 28 +++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 6917820604..746efce67f 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -95,6 +95,21 @@ class Q(tree.Node): query.promote_joins(joins) return clause + def flatten(self): + """ + Recursively yield this Q object and all subexpressions, in depth-first + order. + """ + yield self + for child in self.children: + if isinstance(child, tuple): + # Use the lookup. + child = child[1] + if hasattr(child, "flatten"): + yield from child.flatten() + else: + yield child + def deconstruct(self): path = "%s.%s" % (self.__class__.__module__, self.__class__.__name__) if path.startswith("django.db.models.query_utils"): diff --git a/tests/queries/test_q.py b/tests/queries/test_q.py index 39645a6f31..42a00da3eb 100644 --- a/tests/queries/test_q.py +++ b/tests/queries/test_q.py @@ -1,5 +1,15 @@ -from django.db.models import BooleanField, Exists, F, OuterRef, Q +from django.db.models import ( + BooleanField, + Exists, + ExpressionWrapper, + F, + OuterRef, + Q, + Value, +) from django.db.models.expressions import RawSQL +from django.db.models.functions import Lower +from django.db.models.sql.where import NothingNode from django.test import SimpleTestCase from .models import Tag @@ -188,3 +198,19 @@ class QTests(SimpleTestCase): q = q1 & q2 path, args, kwargs = q.deconstruct() self.assertEqual(Q(*args, **kwargs), q) + + def test_flatten(self): + q = Q() + self.assertEqual(list(q.flatten()), [q]) + q = Q(NothingNode()) + self.assertEqual(list(q.flatten()), [q, q.children[0]]) + q = Q( + ExpressionWrapper( + Q(RawSQL("id = 0", params=(), output_field=BooleanField())) + | Q(price=Value("4.55")) + | Q(name=Lower("category")), + output_field=BooleanField(), + ) + ) + flatten = list(q.flatten()) + self.assertEqual(len(flatten), 7)