Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion django/db/models/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from django.db import DatabaseError, NotSupportedError, connection
from django.db.models import fields
from django.db.models.constants import LOOKUP_SEP
from django.db.models.query_utils import Q
from django.db.models.query_utils import PROHIBITED_FILTER_KWARGS, Q
from django.utils.deconstruct import deconstructible
from django.utils.functional import cached_property, classproperty
from django.utils.hashable import make_hashable
Expand Down Expand Up @@ -1640,6 +1640,9 @@ class When(Expression):

def __init__(self, condition=None, then=None, **lookups):
if lookups:
if invalid_kwargs := PROHIBITED_FILTER_KWARGS.intersection(lookups):
invalid_str = ", ".join(f"'{k}'" for k in sorted(invalid_kwargs))
raise TypeError(f"The following kwargs are invalid: {invalid_str}")
if condition is None:
condition, lookups = Q(**lookups), None
elif getattr(condition, "conditional", False):
Expand Down
4 changes: 1 addition & 3 deletions django/db/models/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from django.db.models.expressions import Case, DatabaseDefault, F, OrderBy, Value, When
from django.db.models.fetch_modes import FETCH_ONE
from django.db.models.functions import Cast, Trunc
from django.db.models.query_utils import FilteredRelation, Q
from django.db.models.query_utils import PROHIBITED_FILTER_KWARGS, FilteredRelation, Q
from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE, ROW_COUNT
from django.db.models.utils import (
AltersData,
Expand All @@ -46,8 +46,6 @@
# The maximum number of items to display in a QuerySet.__repr__
REPR_OUTPUT_SIZE = 20

PROHIBITED_FILTER_KWARGS = frozenset(["_connector", "_negated"])


class BaseIterable:
def __init__(
Expand Down
2 changes: 2 additions & 0 deletions django/db/models/query_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

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

PROHIBITED_FILTER_KWARGS = frozenset(["_connector", "_negated"])

# PathInfo is used when converting lookups (fk__somecol). The contents
# describe the relation in Model terms (model Options and Fields for both
# sides of the relation. The join_field is the field backing the relation.
Expand Down
5 changes: 5 additions & 0 deletions tests/expressions_case/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,11 @@ def test_invalid_when_constructor_args(self):
with self.assertRaisesMessage(TypeError, msg):
When()

def test_when_rejects_invalid_arguments(self):
msg = "The following kwargs are invalid: '_connector', '_negated'"
with self.assertRaisesMessage(TypeError, msg):
When(_negated=True, _connector="evil")

def test_empty_q_object(self):
msg = "An empty Q() can't be used as a When() condition."
with self.assertRaisesMessage(ValueError, msg):
Expand Down
10 changes: 6 additions & 4 deletions tests/modeladmin/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,8 @@ class ConcertAdmin(ModelAdmin):
'class="related-widget-wrapper" data-model-ref="band"><select '
'name="main_band" data-context="available-source" required '
'id="id_main_band" data-custom-widget="true" multiple>'
'<option value="">---------</option><option value="1">The Doors</option>'
'<option value="">---------</option>'
f'<option value="{self.band.pk}">The Doors</option>'
"</select></div></div>"
)
self.assertInHTML(expected, cmafa().render())
Expand All @@ -873,9 +874,10 @@ class ConcertAdmin(ModelAdmin):
expected = (
'<fieldset><legend>Main band:</legend><div class="related-widget-wrapper" '
'data-model-ref="band"><div id="id_main_band"><div><label '
'for="id_main_band_0"><input type="radio" name="main_band" value="1" '
'data-context="available-source" required id="id_main_band_0" '
'use_fieldset="true">The Doors</label></div></div></div></fieldset>'
'for="id_main_band_0"><input type="radio" name="main_band" '
f'value="{self.band.pk}" data-context="available-source" '
'required id="id_main_band_0" use_fieldset="true">The Doors</label>'
"</div></div></div></fieldset>"
)
self.assertInHTML(expected, cmafa().render())

Expand Down
Loading