diff --git a/django/db/models/base.py b/django/db/models/base.py
index 71fd1f7..feaceee 100644
a
|
b
|
class Model(object):
|
382 | 382 | return '%s object' % self.__class__.__name__ |
383 | 383 | |
384 | 384 | def __eq__(self, other): |
385 | | return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val() |
| 385 | return isinstance(other, Model) and \ |
| 386 | self._meta.proxy_for_model == other._meta.proxy_for_model and \ |
| 387 | self._get_pk_val() == other._get_pk_val() |
386 | 388 | |
387 | 389 | def __ne__(self, other): |
388 | 390 | return not self.__eq__(other) |
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 6f0f406..00a5586 100644
a
|
b
|
class Options(object):
|
64 | 64 | self.object_name = cls.__name__ |
65 | 65 | self.module_name = self.object_name.lower() |
66 | 66 | self.verbose_name = get_verbose_name(self.object_name) |
| 67 | # Note: for convenience we set that every concrete class and abstract class |
| 68 | # is proxy of itself. This helps in __eq__ checks for example. |
| 69 | self.proxy_for_model = cls |
67 | 70 | |
68 | 71 | # Next, apply any overridden values from 'class Meta'. |
69 | 72 | if self.meta: |
diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt
index 0e18205..ec3324d 100644
a
|
b
|
There are three styles of inheritance that are possible in Django.
|
795 | 795 | without changing the models fields in any way, you can use |
796 | 796 | :ref:`proxy-models`. |
797 | 797 | |
| 798 | A note about model instance equality operator |
| 799 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 800 | |
| 801 | When comparing the equality of model instances, Django uses the following |
| 802 | rules. Two instances are considered equal if: |
| 803 | |
| 804 | 1. The primary keys are equal. Note that if both instances have primary key value of None, they are considered equal. |
| 805 | 2. The instances are of same class. For this rule, proxy models are considered instances of the class they are proxying. Multi-table inheriting instance is not considered to be of same class with its base class. |
| 806 | |
798 | 807 | .. _abstract-base-classes: |
799 | 808 | |
800 | 809 | Abstract base classes |
diff --git a/tests/modeltests/defer/tests.py b/tests/modeltests/defer/tests.py
index 5f6c53d..9cf32ee 100644
a
|
b
|
class DeferTests(TestCase):
|
23 | 23 | p1 = Primary.objects.create(name="p1", value="xx", related=s1) |
24 | 24 | |
25 | 25 | qs = Primary.objects.all() |
26 | | |
27 | 26 | self.assert_delayed(qs.defer("name")[0], 1) |
28 | 27 | self.assert_delayed(qs.only("name")[0], 2) |
29 | 28 | self.assert_delayed(qs.defer("related__first")[0], 0) |
| 29 | |
| 30 | # deferred instances are equal to their non-deferred counterpart |
| 31 | deferred_p1 = qs.defer("name")[0] |
| 32 | self.assertTrue(deferred_p1==p1) |
| 33 | # The __eq__ operator is symmetric as well as the == operator |
| 34 | self.assertTrue(p1==deferred_p1) |
| 35 | self.assertTrue(deferred_p1.__eq__(p1)) |
| 36 | self.assertTrue(p1.__eq__(deferred_p1)) |
| 37 | |
30 | 38 | |
31 | 39 | obj = qs.select_related().only("related__first")[0] |
32 | 40 | self.assert_delayed(obj, 2) |
diff --git a/tests/modeltests/model_inheritance/tests.py b/tests/modeltests/model_inheritance/tests.py
index 334297a..1b19dcb 100644
a
|
b
|
class ModelInheritanceTests(TestCase):
|
69 | 69 | StudentWorker.objects.get, pk__lt=sw2.pk + 100 |
70 | 70 | ) |
71 | 71 | |
| 72 | # A multi-table inherited instance is never cosidered equal to |
| 73 | # its base class instances. |
| 74 | w = Worker(pk=1) |
| 75 | sw = StudentWorker(pk=1) |
| 76 | self.assertFalse(w==sw) |
| 77 | # The __eq__ operator is symmetric as well as the '==' operator |
| 78 | self.assertFalse(sw==w) |
| 79 | self.assertFalse(w.__eq__(sw)) |
| 80 | self.assertFalse(sw.__eq__(w)) |
| 81 | |
72 | 82 | def test_multiple_table(self): |
73 | 83 | post = Post.objects.create(title="Lorem Ipsum") |
74 | 84 | # The Post model has distinct accessors for the Comment and Link models. |
diff --git a/tests/modeltests/proxy_models/tests.py b/tests/modeltests/proxy_models/tests.py
index 0a46a25..a4c7d40 100644
a
|
b
|
class ProxyModelTests(TestCase):
|
45 | 45 | self.assertEqual(MyPerson.objects.get(name="Foo McBar").id, person.id) |
46 | 46 | self.assertFalse(MyPerson.objects.get(id=person.id).has_special_name()) |
47 | 47 | |
| 48 | def test_proxy_eq(self): |
| 49 | """ |
| 50 | Proxied models are considered equal to their concrete base class. |
| 51 | """ |
| 52 | p = Person(pk=1) |
| 53 | mp = MyPerson(pk=1) |
| 54 | self.assertTrue(p==mp) |
| 55 | # the __eq__ operator is symmetric as well as the == operator |
| 56 | self.assertTrue(mp==p) |
| 57 | self.assertTrue(p.__eq__(mp)) |
| 58 | self.assertTrue(mp.__eq__(p)) |
| 59 | |
48 | 60 | def test_no_proxy(self): |
49 | 61 | """ |
50 | 62 | Person is not proxied by StatusPerson subclass. |