Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django 1.7: Passing around unsaved instances throws "unhashable" exception

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
like image 776
fangsterr Avatar asked Feb 11 '23 22:02

fangsterr


1 Answers

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

like image 133
Kevin Christopher Henry Avatar answered Feb 14 '23 13:02

Kevin Christopher Henry