使用像post_save
这样的Django信号时,可以通过执行以下操作来阻止它在首次创建对象时触发:
@receiver(post_save,sender=MyModel)
def my_signal(sender, instance, created,**kwargs):
if not created:
pass # Do nothing, as the item is new.
else:
logger.INFO("The item changed - %s"%(instance) )
但是,在最初创建项目后so no such argument is passed in会应用ManyToMany关系,这使得在这些情况下难以抑制。
@receiver(m2m_changed,sender=MyModel.somerelation.though)
def my_signal(sender, instance, created,**kwargs):
if __something__: # What goes here?
pass # Do nothing, as the item is new.
else:
logger.INFO("The item changed - %s"%(instance) )
是否有一种简单的方法可以在刚刚创建的对象上对m2m_changed
信号进行抑制?
答案 0 :(得分:1)
我认为没有简单的方法可以做到这一点。
正如Django doc所说,在项目被保存之前,您无法将项目与关系相关联。 doc:
中的示例>>> a1 = Article(headline='...')
>>> a1.publications.add(p1)
Traceback (most recent call last):
...
ValueError: 'Article' instance needs to have a primary key value before a many-to-many relationship can be used.
# should save Article first
>>> a1.save()
# the below statement never know it's just following a creation or not
>>> a1.publications.add(p1)
关系记录在逻辑上不可能知道它是否被添加到"刚刚创建的项目"或者"一段时间内已存在的项目",没有外部信息。
我想出了一些解决方法:
解决方案1.在MyModel
中添加DatetimeField以指示创建时间。 m2m_changed处理程序使用创建时间来检查创建项目的时间。它在某些情况下实际工作,但不能保证正确性
解决方案2.添加'创建' MyModel
中的属性,可以在post_save处理程序中,也可以在其他代码中。例如:
@receiver(post_save, sender=Pizza)
def pizza_listener(sender, instance, created, **kwargs):
instance.created = created
@receiver(m2m_changed, sender=Pizza.toppings.through)
def topping_listener(sender, instance, action, **kwargs):
if action != 'post_add':
# as example, only handle post_add action here
return
if getattr(instance, 'created', False):
print 'toppings added to freshly created Pizza'
else:
print 'toppings added to modified Pizza'
instance.created = False
演示:
p1 = Pizza.objects.create(name='Pizza1')
p1.toppings.add(Topping.objects.create())
>>> toppings added to freshly created Pizza
p1.toppings.add(Topping.objects.create())
>>> toppings added to modified Pizza
p2 = Pizza.objects.create(name='Pizza2')
p2.name = 'Pizza2-1'
p2.save()
p2.toppings.add(Topping.objects.create())
>>> toppings added to modified Pizza
但要小心使用此解决方案。自从'创建'属性已分配给Python实例,未保存在DB中,可能会出错:
p3 = Pizza.objects.create(name='Pizza3')
p3_1 = Pizza.objects.get(name='Pizza3')
p3_1.toppings.add(Topping.objects.create())
>>> toppings added to modified Pizza
p3.toppings.add(Topping.objects.create())
>>> toppings added to freshly created Pizza
这完全取决于答案。然后,抓住你了!我是来自github django-notifications组的zhang-z:)
答案 1 :(得分:0)
@ZZY的回答基本上帮助我意识到,如果不存储其他字段,这是不可能的。幸运的是,我使用的django-model-utils包含TimeStampedModel
,其中包含created
字段。
提供足够小的delta,在捕获信号时检查创建的时间相对容易。
@receiver(m2m_changed,sender=MyModel.somerelation.though)
def my_signal(sender, instance, created,**kwargs):
if action in ['post_add','post_remove','post_clear']:
created = instance.created >= timezone.now() - datetime.timedelta(seconds=5)
if created:
logger.INFO("The item changed - %s"%(instance) )
答案 2 :(得分:0)
检查对象是否创建的更简单快捷的方法是使用 _state.adding 属性:
def m2m_change_method(sender, **kwargs):
instance = kwargs.pop('instance', None)
if instance:
if instance.adding: #created object
pk_set = list(kwargs.pop('pk_set')) #ids of object added to m2m relation
else:
# do something if the instance not newly created or changed
# if you want to check if the m2m objects is new use pk_set query if exists()
m2m_change.connect(m2m_change_method, sender=YourModel.many_to_many_field.through)