[Answered ]-Using django encoded strings as a sender for django signals

2👍

I ran into a similar problem with signals and found what I think is a better solution.

I definitely prefer using the sender argument of connect to checks inside the handler if possible.

The problem as you say is object id(): two strings (not even just unicode strings!) with the same content may have different object ids. The solution is the intern() built-in which enters the given string into python’s internal identifier table (this is very similar to ruby’s Symbol).

If you use sender=intern(sender_string) on both send() and connect() things should work as expected.

Two important caveats:

  1. intern() only works on str, not unicode – you will have to deal with encoding back to str, and you have to perform the same encoding for both send() and connect().
  2. Python’s internal record of your interned string can be discarded by the garbage collector when it discards the reference to your interned string, so you have to make sure you keep it around.

A good way to deal with both problems is that you are probably only interested in signals from a few pre-defined strings, so just stick those in a configuration constant, already interned.

For example:

user_did_something = Signal(providing_args=["data"])

class User(models.Model):
    identifier = models.CharField(max_length=32)
    def send_signal(self):
        user_did_something.send(sender=intern(self.example_field.encode('utf8')), data=self) 


ADMIN_USER = intern('admin')
BIG_ADMIN_USER = intern(u'größer admin'.encode('utf8'))


user_did_something.connect(admin_behavior, sender=ADMIN_USER)
user_did_something.connect(big_admin_behavior, sender=BIG_ADMIN_USER)

BIG_ADMIN_USER will print out to gibberish if not decoded back to a unicode string, but I suspect most such identifiers will be ascii.

0👍

I think it would make more sense if you check the string in the handler:

from django.dispatch import Signal
from django.db import models

example_signal = Signal(providing_args=["instance"])

class Example(models.Model):
    example_field = models.CharField(max_length=32)
    def send_signal(self):
        example_signal.send(sender=self.__class__, instance=self)

def example_handler(sender, instance):
    if instance.example_field == u'something':
        pass#do something

example_signal.connect(example_handler, sender=Example)

Leave a comment