I'm migrating over to Django 1.7 currently. I have some signals that pass an unsaved model instance, which now throws TypeError: Model instances without primary key value are unhashable
.
I'm wondering how does Django pre_save
signal pass around the instance then? I'm looking around the docs and even found the commit which implemented this in 1.7 (https://github.com/django/django/commit/6af05e7a0f0e4604d6a67899acaa99d73ec0dfaa), and I just have no idea how it even works.
Could someone explain to me either how pre_save gets around this or how I can get around this limitation myself? Thanks.
Example code below:
from django.dispatch import Signal
send_text = Signal()
unsaved_model = SomeModel() # note that neither `create` or `.save()` are being called
send_text.send(sender=unsaved_model) # error gets thrown when this gets called
Traceback:
File "/home/ubuntu/fangsterr-app/notifications/models.py", line 43, in send
send_text.send(sender=self)
File "/home/ubuntu/virtualenvs/venv-2.7.5/lib/python2.7/site-packages/django/dispatch/dispatcher.py", line 194, in send
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
File "/home/ubuntu/virtualenvs/venv-2.7.5/lib/python2.7/site-packages/django/db/models/base.py", line 484, in __hash__
raise TypeError("Model instances without primary key value are unhashable")
TypeError: Model instances without primary key value are unhashable
It looks like Django stores the
sender
in a cache that it uses for lookup during signal dispatch. That requires thesender
to be hashable, which doesn't work on a model instance without apk
.The reason this doesn't affect
pre_save
et al. is that, by convention, thesender
is the model class, not the model instance. Theinstance
is passed in its own argument. See the documentation.The solution is easy—use the class as the
sender
, and pass the instance as an argument.(If this worked before it was just happenstance. The bug that was fixed caused all unsaved model instances to evaluate as equal.)