#30333: __hash__ is not inherited from model.Model if __eq__ is defined
-------------------------------------+-------------------------------------
Reporter: Adam Janik | Owner: Carlton
| Gibson
Type: Bug | Status: closed
Component: Database layer | Version: 2.2
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Carlton Gibson):
* status: assigned => closed
* resolution: => duplicate
* severity: Release blocker => Normal
Comment:
Ultimately this is a slightly different version of #30254, which was
raised after a68ea231012434b522ce45c513d84add516afa60.
(Will resolve as a duplicate.)
Prior to the changes in a68ea231012434b522ce45c513d84add516afa60 the model
would
inherit **both** `__eq__()` and `__hash__()` from `models.Model` during
`__new__()`.
(With `__eq__()` then being replaced in the `add_to_class()` call later.)
This was incorrect. The Python docs are quite specific:
> A class that overrides __eq__() and does not define __hash__() will have
its __hash__() implicitly set to None. When the __hash__() method of a
class is None, instances of the class will raise an appropriate TypeError
when a program attempts to retrieve their hash value, and will also be
correctly identified as unhashable when checking isinstance(obj,
collections.abc.Hashable).
>
> If a class that overrides __eq__() needs to retain the implementation of
__hash__() from a parent class, the interpreter must be told this
explicitly by setting __hash__ = <ParentClass>.__hash__.
By passing the defined `__eq__()` (as well as `__hash__()` if defined)
into `__new__()`, the change is bringing Django's behaviour into line with
Python's.
The changes here were a bug fix, rather than a breaking change per se.
(Yes, it **is** a
change in behaviour, but so are all bugfixes, and we don't list them as
breaking changes in the release notes.)
I've [https://github.com/django/django/pull/11184 added a couple test
cases demonstrating the expected behaviour in a PR]. In particular the
`test_missing_hash_not_inherited()` cases fails on Django 2.1.
The fix for your project is to explicitly declare the `__hash__ `
attribute as per the docs above:
{{{
__hash__ = models.Model.__hash__
}}}
I hope that helps.
--
Ticket URL: <https://code.djangoproject.com/ticket/30333#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/065.242279d00db9da85e58c22ef8fc2fc45%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.