I'm testing the update of an application from Django 2.1.7 to 2.2.12. I got an error when running my unit tests, which boils down to a model object not being hashable :
Station.objects.all().delete()
py37\lib\site-packages\django\db\models\query.py:710: in delete
collector.collect(del_query)
py37\lib\site-packages\django\db\models\deletion.py:192: in collect
reverse_dependency=reverse_dependency)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <django.db.models.deletion.Collector object at 0x000001EC78243E80>
objs = <QuerySet [<Station(nom='DUNKERQUE')>, <Station(nom='STATION1')>, <Station(nom='STATION2')>]>, source = None, nullable = False
reverse_dependency = False
def add(self, objs, source=None, nullable=False, reverse_dependency=False):
"""
Add 'objs' to the collection of objects to be deleted. If the call is
the result of a cascade, 'source' should be the model that caused it,
and 'nullable' should be set to True if the relation can be null.
Return a list of all objects that were not already collected.
"""
if not objs:
return []
new_objs = []
model = objs[0].__class__
instances = self.data.setdefault(model, set())
for obj in objs:
> if obj not in instances:
E TypeError: unhashable type: 'Station'
Instances of model objects are hashable in Django, once they are saved to database and get a primary key.
I don't understand where the error comes from and why I get this when running this basic code:
In [7]: s = Station.objects.create(nom='SOME PLACE')
In [8]: hash(s)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-9333020f3184> in <module>
----> 1 hash(s)
TypeError: unhashable type: 'Station'
In [9]: s.pk
Out[9]: 2035
All this code works fine when I switch back to Django 2.1.7. The same happens with other model objects in the app. I'm using python version 3.7.2 on Windows, with a SQlite backend (on the development workstation).
Edit: Here's the definition of the model referred to above:
class Station(models.Model):
nom = models.CharField(max_length=200, unique=True)
def __str__(self):
return self.nom
def __repr__(self):
return "<Station(nom='{}')>".format(self.nom)
def __eq__(self, other):
return isinstance(other, Station) and self.nom == other.nom
__eq__
in yourStation
model? – Gianna__eq__
. Overriding__hash__
fixes the problem. Do you want to write an answer so I can give you credit ? – Handknit__eq__
but not__hash__
has a hash set to None), the fix suggested in the ticket doesn't seem appropriate to me : every instance would return the same hash (unless I'm missing something), which is of course not the correct behaviour. – Handknit__hash__ = models.Model.__hash__
in Django 2.2, thenStudent(pk=1)
andStudent(pk=2)
will have different hashes.Student(pk=1)
andOtherModel(pk=1)
will have the same hash, because theModel.__hash__
method returnshash(self.pk)
. That's the same behaviour in Django 2.1. If you want different models with the same pk to return different hashes then you need to override__hash__
in Django 2.1 and 2.2. – Gianna__hash__
rather than reassigning it, but got it wrong. All clear, thanks. – Handknit