首页 > 解决方案 > Django:如何确定一个对象是否被任何其他对象引用?

问题描述

概括

在 Django 中,确定数据库中是否有引用给定对象的对象的最简单方法是什么?

细节

考虑 Django 的相关对象参考中的这个最小示例:

from django.db import models

class Reporter(models.Model):
    pass

class Article(models.Model):
    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)

我们如何确定是否有任何对象(不仅仅是 对象)通过 a 、 a或 aArticle指向给定对象?ReporterOneToOneFieldForeignKeyManyToManyField

换句话说,我们想确定给定对象是否有任何反向关系。

因为Article这很简单,我们可以只得到 eg reporter.article_set.count(),但以后可能会添加其他也指向 的模型Reporter,这些也必须考虑在内。

示例用例

一个示例用例是我们希望在对象被任何其他对象引用时立即阻止修改。或者我们可以使用它来强制执行一种类似于on_delete=models.PROTECT机制的行为。

标签: pythondjangoforeign-keys

解决方案


Model._meta 这是一个使用API的工作解决方案,但我不确定这是否是最好的方法。希望得到更好的答案。

基本上,给定一个对象,我们得到一个它的反向关系列表,然后,对于其中的每一个,我们检查关系中是否有任何对象。

# just any given reporter object
obj = Reporter.objects.first()
# assume no references to obj
obj_has_reverse = False
# skip for new objects (i.e. those not yet saved to database)
if obj.id is not None:  
    # reverse relation "fields" on the Reporter model are auto-created and
    # not concrete
    for reverse in [f for f in obj._meta.get_fields() 
                    if f.auto_created and not f.concrete]:
        # in case the related name has been customized
        name = reverse.get_accessor_name()
        # one-to-one requires a special approach
        has_reverse_one_to_one = reverse.one_to_one and hasattr(obj, name)
        has_reverse_other = not reverse.one_to_one and getattr(obj, name).count()
        if has_reverse_one_to_one or has_reverse_other:
            obj_has_reverse = True

请注意,ForeignKeyManyToManyField返回一个相关管理器的反向关系,所以我们可以检查例如count(). 但是,反向关系 forOneToOneField不会返回 a RelatedManagerDoesNotExist如果没有相关对象,它会引发异常,如docs中所述。另请参阅的来源reverse_related


推荐阅读