[2.2.x] Fixed #30628 -- Adjusted expression identity to differentiate bound fields.

Expressions referring to different bound fields should not be
considered equal.

Thanks Julien Enselme for the detailed report.

Regression in bc7e288ca9.

Backport of ee6e93ec87 from master
This commit is contained in:
Simon Charette 2019-07-09 17:26:37 -04:00 committed by Mariusz Felisiak
parent 8f0b9e7f9a
commit 9dee8515d6
6 changed files with 37 additions and 5 deletions

View File

@ -372,7 +372,10 @@ class BaseExpression:
identity = [self.__class__]
for arg, value in arguments:
if isinstance(value, fields.Field):
value = type(value)
if value.name and value.model:
value = (value.model._meta.label, value.name)
else:
value = type(value)
else:
value = make_hashable(value)
identity.append((arg, value))

View File

@ -9,4 +9,6 @@ Django 2.2.4 fixes several bugs in 2.2.3.
Bugfixes
========
* ...
* Fixed a regression in Django 2.2 when ordering a ``QuerySet.union()``,
``intersection()``, or ``difference()`` by a field type present more than
once results in the wrong ordering being used (:ticket:`30628`).

View File

@ -21,7 +21,7 @@ from django.db.models.functions import (
from django.db.models.sql import constants
from django.db.models.sql.datastructures import Join
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
from django.test.utils import Approximate
from django.test.utils import Approximate, isolate_apps
from .models import (
UUID, UUIDPK, Company, Employee, Experiment, Number, RemoteEmployee,
@ -839,6 +839,7 @@ class ExpressionsTests(TestCase):
)
@isolate_apps('expressions')
class SimpleExpressionTests(SimpleTestCase):
def test_equal(self):
@ -852,6 +853,15 @@ class SimpleExpressionTests(SimpleTestCase):
Expression(models.CharField())
)
class TestModel(models.Model):
field = models.IntegerField()
other_field = models.IntegerField()
self.assertNotEqual(
Expression(TestModel._meta.get_field('field')),
Expression(TestModel._meta.get_field('other_field')),
)
def test_hash(self):
self.assertEqual(hash(Expression()), hash(Expression()))
self.assertEqual(
@ -863,6 +873,15 @@ class SimpleExpressionTests(SimpleTestCase):
hash(Expression(models.CharField())),
)
class TestModel(models.Model):
field = models.IntegerField()
other_field = models.IntegerField()
self.assertNotEqual(
hash(Expression(TestModel._meta.get_field('field'))),
hash(Expression(TestModel._meta.get_field('other_field'))),
)
class ExpressionsNumericTests(TestCase):

View File

@ -143,6 +143,7 @@ class Cover(models.Model):
class Number(models.Model):
num = models.IntegerField()
other_num = models.IntegerField(null=True)
def __str__(self):
return str(self.num)

View File

@ -9,7 +9,7 @@ from .models import Number, ReservedName
class QuerySetSetOperationTests(TestCase):
@classmethod
def setUpTestData(cls):
Number.objects.bulk_create(Number(num=i) for i in range(10))
Number.objects.bulk_create(Number(num=i, other_num=10 - i) for i in range(10))
def number_transform(self, value):
return value.num
@ -225,3 +225,10 @@ class QuerySetSetOperationTests(TestCase):
qs1 = Number.objects.all()
qs2 = Number.objects.intersection(Number.objects.filter(num__gt=1))
self.assertEqual(qs1.difference(qs2).count(), 2)
def test_order_by_same_type(self):
qs = Number.objects.all()
union = qs.union(qs)
numbers = list(range(10))
self.assertNumbersEqual(union.order_by('num'), numbers)
self.assertNumbersEqual(union.order_by('other_num'), reversed(numbers))

View File

@ -2345,7 +2345,7 @@ class ValuesQuerysetTests(TestCase):
qs = Number.objects.extra(select={'num2': 'num+1'}).annotate(Count('id'))
values = qs.values_list(named=True).first()
self.assertEqual(type(values).__name__, 'Row')
self.assertEqual(values._fields, ('num2', 'id', 'num', 'id__count'))
self.assertEqual(values._fields, ('num2', 'id', 'num', 'other_num', 'id__count'))
self.assertEqual(values.num, 72)
self.assertEqual(values.num2, 73)
self.assertEqual(values.id__count, 1)