﻿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
35459	Case.extra is undocumented, untested, and can hide potential issues	Baptiste Mispelon	Priyank Panchal	"I came across this today while accidentally doing something like the following:
{{{#!python
MyModel.objects.annotate(x=Case(When(somefield=123), then=456, default=789))
}}}

The query ran without errors and produced results but they were slightly wrong. It took me longer than I'd like to admit to realize that I should have written:
{{{#!python
MyModel.objects.annotate(x=Case(When(somefield=123, then=456), default=789))
}}}

(in case you hadn't seen the difference, the `then` kwarg was passed to `Case` instead of `When`).

Once I figured out what was going on, I was surprised that Django had accepted the `then` argument for `Case`. I would have expected a `TypeError`.

The documentation [1] does mention that the signature is `class Case(*cases, **extra)`, but doesn't explain what happens to `**extra`, and even later refers to ""the default keyword argument"" which seems inconsistent.

To top it all of, it seems that the feature is untested. I removed `**extra` from `Case.__init__` and `Case.as_sql()`, ran the full test suite (under sqlite), and got zero errors:
{{{#!diff
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 4ee22420d9..34308fad9c 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -1562,13 +1562,12 @@ class Case(SQLiteNumericMixin, Expression):
     template = ""CASE %(cases)s ELSE %(default)s END""
     case_joiner = "" ""
 
-    def __init__(self, *cases, default=None, output_field=None, **extra):
+    def __init__(self, *cases, default=None, output_field=None):
         if not all(isinstance(case, When) for case in cases):
             raise TypeError(""Positional arguments must all be When objects."")
         super().__init__(output_field)
         self.cases = list(cases)
         self.default = self._parse_expressions(default)[0]
-        self.extra = extra
 
     def __str__(self):
         return ""CASE %s, ELSE %r"" % (
@@ -1610,7 +1609,7 @@ class Case(SQLiteNumericMixin, Expression):
         connection.ops.check_expression_support(self)
         if not self.cases:
             return compiler.compile(self.default)
-        template_params = {**self.extra, **extra_context}
+        template_params = {**extra_context}
         case_parts = []
         sql_params = []
         default_sql, default_params = compiler.compile(self.default)
}}}

As far as I can tell, this feature has been present since `Case` was introduced in #24031 (65246de7b1d70d25831ab394c4f4a75813f629fe).

I would be tempted to drop it considering the way it silently swallows unknown kwargs. Am I missing something?


[1] https://docs.djangoproject.com/en/dev/ref/models/conditional-expressions/#case"	Cleanup/optimization	assigned	Database layer (models, ORM)	5.0	Normal			Tim Graham	Accepted	1	0	0	1	0	0
