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. |