使用Django编码字符串作为Django信号的发送者

1 投票
2 回答
1558 浏览
提问于 2025-04-16 10:59

我想用一个字符串作为自定义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模型字符串等价。我尝试了strreprencode('UTF-8')django.util.encodings,但都没有成功。

目前我已经实现了一个变通的方法,但我仍然想理解发生了什么……任何建议都非常感谢!

2 个回答

0

我觉得在处理程序里检查这个字符串会更合理一些:

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

我遇到了一个类似的信号问题,找到了一种我认为更好的解决办法。

我更喜欢在使用 connect 时用 sender 参数,而不是在处理函数内部进行检查。

正如你所说的问题是关于对象的 id():两个内容相同的字符串(甚至不仅仅是unicode字符串!)可能会有不同的对象ID。解决办法是使用 intern() 这个内置函数,它会把给定的字符串放入Python的内部标识符表中(这和Ruby的 Symbol 非常相似)。

如果你在 send()connect() 中都使用 sender=intern(sender_string),事情就应该能按预期工作。

有两个重要的注意事项:

  1. intern() 只适用于 str 类型,而不适用于 unicode - 你需要把 unicode 编码回 str,并且在 send()connect() 中都要进行相同的编码。
  2. 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编码。

撰写回答