Skip to content

Commit 3b161e6

Browse files
varunkasyapjacobtylerwalls
authored andcommitted
Fixed #37016 -- Avoided propagating invalid arguments from When() to Q().
1 parent 123fa3a commit 3b161e6

4 files changed

Lines changed: 12 additions & 4 deletions

File tree

django/db/models/expressions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from django.db import DatabaseError, NotSupportedError, connection
1313
from django.db.models import fields
1414
from django.db.models.constants import LOOKUP_SEP
15-
from django.db.models.query_utils import Q
15+
from django.db.models.query_utils import PROHIBITED_FILTER_KWARGS, Q
1616
from django.utils.deconstruct import deconstructible
1717
from django.utils.functional import cached_property, classproperty
1818
from django.utils.hashable import make_hashable
@@ -1640,6 +1640,9 @@ class When(Expression):
16401640

16411641
def __init__(self, condition=None, then=None, **lookups):
16421642
if lookups:
1643+
if invalid_kwargs := PROHIBITED_FILTER_KWARGS.intersection(lookups):
1644+
invalid_str = ", ".join(f"'{k}'" for k in sorted(invalid_kwargs))
1645+
raise TypeError(f"The following kwargs are invalid: {invalid_str}")
16431646
if condition is None:
16441647
condition, lookups = Q(**lookups), None
16451648
elif getattr(condition, "conditional", False):

django/db/models/query.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from django.db.models.expressions import Case, DatabaseDefault, F, OrderBy, Value, When
3030
from django.db.models.fetch_modes import FETCH_ONE
3131
from django.db.models.functions import Cast, Trunc
32-
from django.db.models.query_utils import FilteredRelation, Q
32+
from django.db.models.query_utils import PROHIBITED_FILTER_KWARGS, FilteredRelation, Q
3333
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, ROW_COUNT
3434
from django.db.models.utils import (
3535
AltersData,
@@ -46,8 +46,6 @@
4646
# The maximum number of items to display in a QuerySet.__repr__
4747
REPR_OUTPUT_SIZE = 20
4848

49-
PROHIBITED_FILTER_KWARGS = frozenset(["_connector", "_negated"])
50-
5149

5250
class BaseIterable:
5351
def __init__(

django/db/models/query_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
logger = logging.getLogger("django.db.models")
2323

24+
PROHIBITED_FILTER_KWARGS = frozenset(["_connector", "_negated"])
25+
2426
# PathInfo is used when converting lookups (fk__somecol). The contents
2527
# describe the relation in Model terms (model Options and Fields for both
2628
# sides of the relation. The join_field is the field backing the relation.

tests/expressions_case/tests.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,6 +1668,11 @@ def test_invalid_when_constructor_args(self):
16681668
with self.assertRaisesMessage(TypeError, msg):
16691669
When()
16701670

1671+
def test_when_rejects_invalid_arguments(self):
1672+
msg = "The following kwargs are invalid: '_connector', '_negated'"
1673+
with self.assertRaisesMessage(TypeError, msg):
1674+
When(_negated=True, _connector="evil")
1675+
16711676
def test_empty_q_object(self):
16721677
msg = "An empty Q() can't be used as a When() condition."
16731678
with self.assertRaisesMessage(ValueError, msg):

0 commit comments

Comments
 (0)