summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnssi Kääriäinen <akaariai@gmail.com>2013-08-07 09:51:32 +0300
committerAnssi Kääriäinen <akaariai@gmail.com>2013-08-19 09:51:28 +0300
commit4668c142dce77c6f29fb75532c1acfa1b2d322ff (patch)
tree0d4bfb51d43879c9e0410e42152542a3c78e828d
parent40909826172374b849ddc9db8e1a8be9a8c3251b (diff)
downloaddjango-4668c142dce77c6f29fb75532c1acfa1b2d322ff.tar.gz
Made Model.__eq__ consider proxy models equivalent
Fixed #11892, fixed #16458, fixed #14492.
-rw-r--r--django/db/models/base.py4
-rw-r--r--docs/ref/models/instances.txt34
-rw-r--r--docs/releases/1.7.txt5
-rw-r--r--tests/basic/tests.py4
-rw-r--r--tests/defer/tests.py6
-rw-r--r--tests/model_inheritance/tests.py5
-rw-r--r--tests/proxy_models/tests.py3
7 files changed, 60 insertions, 1 deletions
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']