Передача аргументов django сигналов - post_save/pre_save
Я работаю над приложением для уведомлений в Django 1.6, и я хочу передать дополнительные аргументы в сигналы Django, такие как post_save
. Я попытался использовать частично из functools, но не повезло.
from functools import partial
post_save.connect(
receiver=partial(notify,
fragment_name="categories_index"),
sender=nt.get_model(),
dispatch_uid=nt.sender
)
notify
функция имеет аргумент ключевого слова fragment_name
, который я хочу передать по умолчанию в свои сигналы.
Любые предложения?
Ответы
Ответ 1
Ваша попытка частичного не работает, потому что по умолчанию эти приемники подключены с использованием слабой ссылки.
В соответствии с Django docs:
Django сохраняет обработчики сигналов как слабые ссылки по умолчанию, поэтому, если ваш обработчик является локальной функцией, это может быть сбор мусора. Чтобы предотвратить это, пропустите слабый = False, когда вы вызываете сигналы connect().
from functools import partial
post_save.connect(
receiver=partial(notify,
fragment_name="categories_index"),
sender=nt.get_model(),
dispatch_uid=nt.sender,
weak=False
)
Включить слабый = False, и это частичное не будет собирать мусор.
Мой первоначальный ответ ниже и взял подход, который не использовал частичный.
Вы можете украсить функцию сохранения сообщений до подключения к ресиверу post_save.
from django.dispatch import receiver
from django.db.models.signals import pre_save, post_save, post_delete
def extra_args(fragment_name, *args, **kwargs):
def inner1(f, *args, **kwargs):
def inner2(sender, instance, **kwargs):
f(sender, instance, fragment_name=fragment_name, **kwargs)
return inner2
return inner1
@receiver(post_save, sender=ExampleModel)
@extra_args(fragment_name="categories_index")
def my_post_save(sender, instance, fragment_name, **kwargs):
print "fragment_name : ", fragment_name
#rest of post save...
Дополнительный внутренний в extra_args предназначен для декораторов, которые принимают параметры.
Если вы хотите сделать это программно, это работает одинаково, но обратите внимание, что вам нужно включить weak=False
, чтобы обернутая функция не была собрана мусором.
receiver(post_save, sender=aSenderClass, weak=False)(extra_args(fragment_name="meep")(my_post_save))
Или без упаковки, но вызывая post_save.connect, как ваша первоначальная попытка с частичным
post_save.connect(extra_args(fragment_name="meepConnect")(my_post_save), sender=Author, weak=False)
Ответ 2
Вы можете определить дополнительные аргументы в пользовательском способе сохранения модели следующим образом:
class MyModel(models.Model):
....
def save(self, *args, **kwargs):
super(MyModel, self).save(*args, **kwargs)
self.my_extra_param = 'hello world'
И получить доступ к этому дополнительному аргументу через экземпляр в приемнике сигнала post_save:
@receiver(post_save, sender=MyModel)
def process_my_param(sender, instance, *args, **kwargs):
my_extra_param = instance.my_extra_param
Ответ 3
Если предопределенные сигналы не подходят, вы всегда можете определить свои собственные.
import django.dispatch
custom_post_save = django.dispatch.Signal(providing_args=[
"sender", "instance", "created", "raw", "using", "update_fields", "fragment_name"
])
Затем в вашей модели вам просто нужно переопределить метод save()
:
from django.db import router
class YourModel(Model):
# Your fields and methods
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
custom_signal_kwargs = {
"sender": self.__class__,
"instance": self,
"created": self.pk is None,
"raw": False, # As docs say, it True only for fixture loading
"using": using or router.db_for_write(self.__class__, instance=self),
"update_fields": update_fields,
"fragment_name": "categories_index" # The thing you want
}
super(YourModel, self).save(force_insert=False, force_update=False, using=None,
update_fields=None)
custom_post_save.send(**custom_signal_kwargs) # Send custom signal
Теперь вам просто нужно подключить этот настраиваемый сигнал к вашему приемнику notify(...)
, и он получит fragment_name
в kwargs.
Ответ 4
Код в Django, ответственный за Signals, определен здесь https://github.com/django/django/blob/master/django/dispatch/dispatcher.py. Посмотрите, как он проверяет приемник? Я подозреваю, что ваши проблемы лежат там. Возможно, что вы хотите - это функция-обертка, которая отличает аргументы, которые должен иметь сигнал, но также устанавливает значение имя_файла.
def fragment_receiver(sender, **kwargs)
return notify(sender, fragment_name="categories_index", **kwargs)