From 54f80430be4a9adf1fc00b4ca17547415fafc69b Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Wed, 28 Feb 2018 18:05:23 +0100 Subject: [PATCH] Fixed #29166 -- Fixed crash in When() expression with a list argument. Thanks Matthew Pava for the report and Tim Graham and Carlton Gibson for reviews. Regression in 19b2dfd1bfe7fd716dd3d8bfa5f972070d83b42f. --- django/db/models/expressions.py | 7 ++++++- docs/releases/2.0.3.txt | 3 +++ tests/expressions_case/tests.py | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 8c67839616..307ee97db5 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -369,7 +369,12 @@ class BaseExpression: def __hash__(self): path, args, kwargs = self.deconstruct() - return hash((path,) + args + tuple(kwargs.items())) + kwargs = kwargs.copy() + output_field = type(kwargs.pop('output_field', None)) + return hash((path, output_field) + args + tuple([ + (key, tuple(value)) if isinstance(value, list) else (key, value) + for key, value in kwargs.items() + ])) class Expression(BaseExpression, Combinable): diff --git a/docs/releases/2.0.3.txt b/docs/releases/2.0.3.txt index c0c9ebb782..348af75bc0 100644 --- a/docs/releases/2.0.3.txt +++ b/docs/releases/2.0.3.txt @@ -21,3 +21,6 @@ Bugfixes * Made ``Q.deconstruct()`` deterministic with multiple keyword arguments (:ticket:`29125`). You may need to modify ``Q``'s in existing migrations, or accept an autogenerated migration. + +* Fixed a regression where a ``When()`` expression with a list argument crashes + (:ticket:`29166`). diff --git a/tests/expressions_case/tests.py b/tests/expressions_case/tests.py index 90aba36031..a688dc073d 100644 --- a/tests/expressions_case/tests.py +++ b/tests/expressions_case/tests.py @@ -1288,6 +1288,24 @@ class CaseDocumentationExamples(TestCase): transform=attrgetter('name', 'account_type') ) + def test_hash(self): + expression_1 = Case( + When(account_type__in=[Client.REGULAR, Client.GOLD], then=1), + default=2, + output_field=models.IntegerField(), + ) + expression_2 = Case( + When(account_type__in=(Client.REGULAR, Client.GOLD), then=1), + default=2, + output_field=models.IntegerField(), + ) + expression_3 = Case(When(account_type__in=[Client.REGULAR, Client.GOLD], then=1), default=2) + expression_4 = Case(When(account_type__in=[Client.PLATINUM, Client.GOLD], then=2), default=1) + self.assertEqual(hash(expression_1), hash(expression_2)) + self.assertNotEqual(hash(expression_2), hash(expression_3)) + self.assertNotEqual(hash(expression_1), hash(expression_4)) + self.assertNotEqual(hash(expression_3), hash(expression_4)) + class CaseWhenTests(SimpleTestCase): def test_only_when_arguments(self):