使用Django编码字符串作为Django信号的发送者
我想用一个字符串作为自定义Django信号的发送者,但遇到了一些与Django模型字符串的unicode编码有关的问题。下面是一个希望能说明问题的简短示例:
from django.dispatch import Signal
from django.db import models
example_signal = Signal(providing_args=["data"])
class Example(models.Model):
example_field = models.CharField(max_length=32)
def send_signal(self):
example_signal.send(sender=self.example_field, data=self) # (arbitrarily using self as the signal payload)
def example_handler(sender, data=None):
print "received data %s" % data
example_signal.connect(example_handler, sender=u'boogat')
... (after entering the django shell and importing the models)
>>> t = Example.objects.create(example_field='boogat')
>>> t.send_signal()
>>>
我浏览了一下Django的dispatcher.py代码 - 在这个情况下,信号调度器似乎使用了Python内置的id函数来为用作发送者的对象生成一个唯一的id。然而,我似乎搞不清楚该如何编程生成一个字符串,这个字符串在传给id()时,能和Django的unicode模型字符串等价。我尝试了str
、repr
、encode('UTF-8')
、django.util.encodings
,但都没有成功。
目前我已经实现了一个变通的方法,但我仍然想理解发生了什么……任何建议都非常感谢!
2 个回答
我觉得在处理程序里检查这个字符串会更合理一些:
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)
我遇到了一个类似的信号问题,找到了一种我认为更好的解决办法。
我更喜欢在使用 connect
时用 sender
参数,而不是在处理函数内部进行检查。
正如你所说的问题是关于对象的 id()
:两个内容相同的字符串(甚至不仅仅是unicode字符串!)可能会有不同的对象ID。解决办法是使用 intern()
这个内置函数,它会把给定的字符串放入Python的内部标识符表中(这和Ruby的 Symbol
非常相似)。
如果你在 send()
和 connect()
中都使用 sender=intern(sender_string)
,事情就应该能按预期工作。
有两个重要的注意事项:
intern()
只适用于str
类型,而不适用于unicode
- 你需要把unicode
编码回str
,并且在send()
和connect()
中都要进行相同的编码。- Python内部对你已处理字符串的记录可能会被垃圾回收器丢弃,当它丢弃对你已处理字符串的引用时,所以你需要确保保持它的存在。
处理这两个问题的一个好方法是,你可能只对几个预定义的字符串的信号感兴趣,所以可以把这些字符串放在一个配置常量中,提前处理好。
例如:
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
如果不解码回 unicode
字符串,会打印出乱码,但我怀疑大多数这样的标识符都是ascii编码。