Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
199 views
in Technique[技术] by (71.8m points)

python - Django CASCADE和post_delete交互(Django CASCADE and post_delete interaction)

I have the following model:

(我有以下模型:)

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)

So I want A to be deleted as well when B is deleted:

(所以我想在删除B时也删除A:)

@receiver(post_delete, sender=B)
def post_delete_b(sender, instance, *args, **kwargs):
  if instance.a:
    instance.a.delete()

And on the deletion of A, I want to delete the objects from the unmanaged databases:

(在删除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)

Now, if I delete object B, it works fine.

(现在,如果删除对象B,它可以正常工作。)

But if I delete obj A, then obj B is deleted by cascade, and then it emits a post_delete signal, which triggers the deletion of A again.

(但是,如果我删除obj A,则obj B被级联删除,然后它发出post_delete信号,该信号再次触发A的删除。)

Django knows how to manage that on his end, so it works fine until it reaches delete_foriegn_obj , which is then called twice and returns a failure on the second attempt.

(Django知道如何管理它,因此它可以正常工作,直到到达delete_foriegn_obj ,然后将其调用两次并在第二次尝试中返回失败。)

I thought about validating that the object exists in delete_foriegn_obj , but it adds 3 more calls to the DB.

(我考虑过验证对象是否存在于delete_foriegn_obj ,但是它向数据库增加了3个调用。)

So the question is: is there a way to know during post_delete_b that object a has been deleted?

(因此问题是:在post_delete_b期间是否有办法知道对象a已被删除?)

Both instance.a and A.objects.get(id=instance.a.id) return the object (I guess Django caches the DB update until it finishes all of the deletions are done).

(instance.aA.objects.get(id=instance.a.id)返回对象(我想Django会缓存数据库更新,直到完成所有删除操作为止)。)

  ask by Aruj translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem is that the cascaded deletions are performed before the requested object is deleted, hence when you queried the DB ( A.objects.get(id=instance.a.id) ) the related a instance is present there.

(的问题是,当你查询的DB(级联缺失所请求的对象被删除之前进行的,因此A.objects.get(id=instance.a.id)相关的a实例是目前。)

instance.a can even show a cached result so there's no way it would show otherwise.

(instance.a甚至可以显示缓存的结果,因此不可能显示否则。)

So while deleting a B model instance, the related A instance will always be existent (if actually there's one).

(因此,在删除B模型实例时,相关的A实例将始终存在(如果实际存在)。)

Hence, from the B model post_delete signal receiver, you can get the related A instance and check if the related B actually exists from DB (there's no way to avoid the DB here to get the actual picture underneath):

(因此,从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()

We also need to make sure we're not getting any cached result by making a._state.fields_cache empty.

(我们还需要通过将a._state.fields_cache空来确保没有得到任何缓存结果。)

The fields_cache (which is actually a descriptor that returns a dict upon first access) is used by the ReverseOneToOneDescriptor (accessor to the related object on the opposite side of a one-to-one) to cache the related field name-value.

(所述fields_cache (这实际上是一个返回的描述符dict在第一接入)用于由ReverseOneToOneDescriptor (存取器上的一对之一的相对侧上的相关对象)来缓存相关领域的名称-值。)

FWIW, the same is done on the forward side of the relationship by the ForwardOneToOneDescriptor accessor.

(FWIW, ForwardOneToOneDescriptor访问器在关系的前端执行相同的操作。)


Edit based on comment:

(根据评论进行编辑:)

If you're using this function for multiple senders' post_delete , you can dynamically get the related attribute via getattr :

(如果将此功能用于多个发件人的post_delete ,则可以通过getattr动态获取相关属性:)

getattr(a, sender.a.field.related_query_name())

this does the same as ab above but allows us to get attribute dynamically via name, so this would result in exactly similar query as you can imagine.

(这和上面的ab ,但是允许我们通过名称动态获取属性,因此这将导致您完全可以想象的类似查询。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...