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.
This commit is contained in:
Simon Charette 2019-07-09 17:26:37 -04:00 committed by Mariusz Felisiak
parent 1bbf77bea5
commit ee6e93ec87
6 changed files with 37 additions and 5 deletions

View File

@ -376,7 +376,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,
@ -898,6 +898,7 @@ class ExpressionsTests(TestCase):
)
@isolate_apps('expressions')
class SimpleExpressionTests(SimpleTestCase):
def test_equal(self):
@ -911,6 +912,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(
@ -922,6 +932,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

@ -148,6 +148,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
@ -251,3 +251,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

@ -2335,7 +2335,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)