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 the sender
to be hashable, which doesn't work on a model instance without a pk
.
The reason this doesn't affect pre_save
et al. is that, by convention, the sender
is the model class, not the model instance. The instance
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With