防止 m2m_changed 在创建对象时触发
Prevent m2m_changed from firing when creating an object
当使用像 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,因此在这些情况下很难抑制。
@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
信号时抑制它?
我认为没有简单的方法可以做到这一点。
正如 Django 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)
在没有外部信息的情况下,逻辑上不可能知道关系记录是添加到 "a just created item" 还是 "an item that already exists for some time"。
我想到的一些解决方法:
解决方法 1.在MyModel
中添加一个DatetimeField来表示创建时间。 m2m_changed 处理程序使用创建时间来检查项目的创建时间。在某些情况下确实有效,但不能保证正确性
解决方案 2. 在 MyModel
中添加一个 'created' 属性,在 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
但请谨慎使用此解决方案。由于 'created' 属性已分配给 Python 实例,未保存在数据库中,因此可能会出错:
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 :)
@ZZY 的回答基本上帮助我意识到如果不存储额外的字段这是不可能的。幸运的是,我使用的是 django-model-utils which includes a TimeStampedModel
,其中包含一个 created
字段。
提供足够小的增量,在捕获信号时检查创建时间相对容易。
@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) )
使用 _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)
当使用像 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,因此在这些情况下很难抑制。
@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
信号时抑制它?
我认为没有简单的方法可以做到这一点。
正如 Django 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)
在没有外部信息的情况下,逻辑上不可能知道关系记录是添加到 "a just created item" 还是 "an item that already exists for some time"。
我想到的一些解决方法:
解决方法 1.在MyModel
中添加一个DatetimeField来表示创建时间。 m2m_changed 处理程序使用创建时间来检查项目的创建时间。在某些情况下确实有效,但不能保证正确性
解决方案 2. 在 MyModel
中添加一个 'created' 属性,在 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
但请谨慎使用此解决方案。由于 'created' 属性已分配给 Python 实例,未保存在数据库中,因此可能会出错:
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 :)
@ZZY 的回答基本上帮助我意识到如果不存储额外的字段这是不可能的。幸运的是,我使用的是 django-model-utils which includes a TimeStampedModel
,其中包含一个 created
字段。
提供足够小的增量,在捕获信号时检查创建时间相对容易。
@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) )
使用 _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)