использование строк в кодировке Django в качестве отправителя для сигналов Django - PullRequest
1 голос
/ 01 февраля 2011

Я хотел бы использовать строку в качестве отправителя для пользовательского сигнала 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 () эквивалентна строке модели Юникода в Django. Я пробовал str, repr, encode('UTF-8'), django.util.encodings, но все безрезультатно.

На данный момент я уже реализовал обходной путь, но я все еще хотел бы понять, что происходит ... любой вклад будет высоко ценится!

Ответы [ 2 ]

2 голосов
/ 18 апреля 2012

Я столкнулся с аналогичной проблемой, связанной с сигналами, и нашел то, что я считаю лучшим решением.

Я определенно предпочитаю использовать аргумент sender для connect для проверки внутри обработчика, если это возможно.

Проблема, как вы говорите, заключается в объекте id(): две строки (даже не просто строки Юникода!) С одинаковым содержимым могут иметь разные идентификаторы объектов.Решением является встроенная функция intern(), которая вводит данную строку во внутреннюю таблицу идентификаторов python (это очень похоже на Symbol в ruby).

Если вы используете sender=intern(sender_string) на send() и connect(), все должно работать как положено.

Два важных предостережения:

  1. intern() работает только на str, а не 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.

0 голосов
/ 01 февраля 2011

Я думаю, что было бы более разумно проверить строку в обработчике:

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