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:
intern()
only works onstr
, notunicode
– you will have to deal with encoding back tostr
, and you have to perform the same encoding for bothsend()
andconnect()
.- 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)