From 4668c142dce77c6f29fb75532c1acfa1b2d322ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Wed, 7 Aug 2013 09:51:32 +0300 Subject: [PATCH] Made Model.__eq__ consider proxy models equivalent Fixed #11892, fixed #16458, fixed #14492. --- django/db/models/base.py | 4 +++- docs/ref/models/instances.txt | 34 ++++++++++++++++++++++++++++++++ docs/releases/1.7.txt | 5 +++++ tests/basic/tests.py | 4 ++++ tests/defer/tests.py | 6 ++++++ tests/model_inheritance/tests.py | 5 +++++ tests/proxy_models/tests.py | 3 +++ 7 files changed, 60 insertions(+), 1 deletion(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index cd3768bccb..3e2ae8d425 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -459,7 +459,9 @@ class Model(six.with_metaclass(ModelBase)): return '%s object' % self.__class__.__name__ def __eq__(self, other): - return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val() + return (isinstance(other, Model) and + self._meta.concrete_model == other._meta.concrete_model and + self._get_pk_val() == other._get_pk_val()) def __ne__(self, other): return not self.__eq__(other) diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index cb8570afdc..94b1face81 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -494,6 +494,40 @@ using ``__str__()`` like this:: # first_name and last_name will be unicode strings. return force_bytes('%s %s' % (self.first_name, self.last_name)) +``__eq__`` +---------- + +.. method:: Model.__eq__() + +The equality method is defined such that instances with the same primary +key value and the same concrete class are considered equal. The term +concrete class means proxy model's first non-proxy parent or the class +itself if it isn't a proxy class. + +For example:: + + form django.db import models + + class MyModel(models.Model): + id = models.AutoField(primary_key=True) + + class MyProxyModel(MyModel): + class Meta: + proxy = True + + class MultitableInherited(MyModel): + pass + + MyModel(id=1) == MyModel(id=1) + MyModel(id=1) == MyProxyModel(id=1) + MyModel(id=1) != MultitableInherited(id=1) + MyModel(id=1) != MyModel(id=2) + +.. versionchanged:: 1.7 + + In previous versions only instances of the exact same class and same + primary key value were considered equal. + ``get_absolute_url`` -------------------- diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index 7a46b3facb..774d9d3161 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -194,6 +194,11 @@ Miscellaneous removes the ability for visitors to generate spurious HTTP 500 errors by requesting static files that don't exist or haven't been collected yet. +* The :meth:`django.db.models.Model.__eq__` method is now defined in a + way where instances of a proxy model and its base model are considered + equal when primary keys match. Previously only instances of exact same + class were considered equal on primary key match. + Features deprecated in 1.7 ========================== diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 55ed6a436f..2b051621ef 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -707,6 +707,10 @@ class ModelTest(TestCase): with self.assertRaises(ObjectDoesNotExist): SelfRef.objects.get(selfref=sr) + def test_eq(self): + self.assertNotEqual(Article(id=1), object()) + self.assertNotEqual(object(), Article(id=1)) + class ConcurrentSaveTests(TransactionTestCase): diff --git a/tests/defer/tests.py b/tests/defer/tests.py index 1df312e1bf..b66b173f7a 100644 --- a/tests/defer/tests.py +++ b/tests/defer/tests.py @@ -183,3 +183,9 @@ class DeferTests(TestCase): with self.assertNumQueries(0): bc_deferred.id self.assertEqual(bc_deferred.pk, bc_deferred.id) + + def test_eq(self): + s1 = Secondary.objects.create(first="x1", second="y1") + s1_defer = Secondary.objects.only('pk').get(pk=s1.pk) + self.assertEqual(s1, s1_defer) + self.assertEqual(s1_defer, s1) diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py index eb50d30cf8..0ba6a15e5d 100644 --- a/tests/model_inheritance/tests.py +++ b/tests/model_inheritance/tests.py @@ -318,3 +318,8 @@ class ModelInheritanceTests(TestCase): sql = query['sql'] if 'UPDATE' in sql: self.assertEqual(expected_sql, sql) + + def test_eq(self): + # Equality doesn't transfer in multitable inheritance. + self.assertNotEqual(Place(id=1), Restaurant(id=1)) + self.assertNotEqual(Restaurant(id=1), Place(id=1)) diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index 240198cc39..3598d65935 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -362,6 +362,9 @@ class ProxyModelTests(TestCase): p = MyPerson.objects.get(pk=100) self.assertEqual(p.name, 'Elvis Presley') + def test_eq(self): + self.assertEqual(MyPerson(id=100), Person(id=100)) + class ProxyModelAdminTests(TestCase): fixtures = ['myhorses']