我有以下模型:
class A():
foriegn_id1 = models.CharField # ref to a database not managed by django
foriegn_id2 = models.CharField
class B():
a = models.OneToOneField(A, on_delete=models.CASCADE)
所以我也希望在删除B时也删除A:
@receiver(post_delete, sender=B)
def post_delete_b(sender, instance, *args, **kwargs):
if instance.a:
instance.a.delete()
并且在删除A时,我想从非托管数据库中删除对象:
@receiver(post_delete, sender=A)
def post_delete_b(sender, instance, *args, **kwargs):
if instance.foriegn_id1:
delete_foriegn_obj_1(instance.foriegn_id1)
if instance.foriegn_id2:
delete_foriegn_obj_2(instance.foriegn_id2)
现在,如果我删除对象B,它可以正常工作。但是,如果我删除obj A,则obj B被级联删除,然后它发出post_delete信号,该信号再次触发A的删除。 Django知道如何管理它,因此它可以正常工作,直到达到delete_foriegn_obj
,然后将其调用两次,并在第二次尝试时返回失败。
我考虑过验证该对象是否存在于delete_foriegn_obj
中,但它又向数据库添加了3个调用。
所以问题是:在post_delete_b
期间是否有办法知道对象a已被删除?instance.a
和A.objects.get(id=instance.a.id)
都返回对象(我想Django会缓存数据库更新,直到完成所有删除操作为止)。
问题是执行级联删除之前删除了所请求的对象,因此,当您查询DB(A.objects.get(id=instance.a.id)
)时,那里存在相关的a
实例。 instance.a
甚至可以显示缓存的结果,因此不可能显示否则。
因此删除B
模型实例时,相关的A
实例将始终存在(如果实际存在)。因此,从B
模型post_delete
信号接收器中,您可以获取相关的A
实例并检查DB中是否确实存在相关的B
(无法避免DB在这里获取下面的实际图片) ):
@receiver(post_delete, sender=B)
def post_delete_b(sender, instance, *args, **kwargs):
try:
a = instance.a
except AttributeError:
return
try:
a._state.fields_cache = {}
except AttributeError:
pass
try:
a.b # one extra query
except AttributeError:
# This is cascaded delete
return
a.delete()
我们还需要通过将a._state.fields_cache
设置为空来确保没有得到任何缓存的结果。 fields_cache
(实际上是第一次访问时返回dict
的描述符)由ReverseOneToOneDescriptor
(一对一相对侧的相关对象的访问者)使用来缓存相关字段名称值。 FWIW,通过ForwardOneToOneDescriptor
访问器在关系的前面也进行了相同的操作。
根据评论进行编辑:
如果您将此功能用于多个发件人的post_delete
,则可以通过getattr
动态获取相关属性:
getattr(a, sender.a.field.related_query_name())
这与上面的a.b
相同,但是允许我们通过名称动态获取属性,因此这将导致您可以想象的完全相似的查询。