如何获得模型的相关对象和模型的子相关对象的总数?

Posted

技术标签:

【中文标题】如何获得模型的相关对象和模型的子相关对象的总数?【英文标题】:How can I get a total count of a model's related objects and the model's children's related objects? 【发布时间】:2011-01-10 04:11:32 【问题描述】:

在 Django 中,我有一个 Checkout 模型,它是某人检查设备的票。我还有一个与结帐模型相关的组织单元模型(通过 ForeignKey),因为结帐的人属于我们校园的一个组织单元。

OrganizationalUnit 具有自关系,因此多个 OU 可以是某个 OU 的子级,而这些子级可以有子级,以此类推。这是模型,有些简化。

class OrganizationalUnit(models.Model):
    name = models.CharField(max_length=100)
    parent = models.ForeignKey(
        'self',
        blank=True, null=True,
        related_name='children',
)

class Checkout(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    department = models.ForeignKey(
        OrganizationalUnit,
        null=True,
        blank=True,
        related_name='checkouts',
)

我想获得与某个组织单位及其所有子项相关的结帐计数。我知道如何获取与 OU 相关的所有结帐的计数。

ou = OrganizationalUnit.objects.get(pk=1)
count = ou.checkouts.all().count()

但是如何使该计数反映此 OU 的子项及其子项的结帐?我是否使用某种迭代循环?


编辑:我想我仍然无法完全理解 while 命令来执行此操作。组织单元可以嵌套到用户想要嵌套的深度,但现在它在 DB 中的深度最多为 5 层。这是我写的……

for kid in ou.children.all():
    child_checkout_count += kid.checkouts.all().count()
    for kid2 in kid.children.all():
        child_checkout_count += kid2.checkouts.all().count()
        for kid3 in kid2.children.all():
            child_checkout_count += kid3.checkouts.all().count()
            for kid4 in kid3.children.all():
                child_checkout_count += kid4.checkouts.all().count()
                for kid5 in kid4.children.all():
                    child_checkout_count += kid5.checkouts.all().count()

……这完全是废话。而且它需要一段时间才能运行,因为它几乎遍历了数据库的主要部分。帮助! (我今天好像不能好好思考。)

【问题讨论】:

【参考方案1】:

我不确定 SQL 在这个上的执行情况如何,但您想要做的正是您解释的。

使用 While 循环获取所有 OU 及其父级,然后计算 Checkouts 并将它们相加。

ORM 为您带来基于 SQL 的动态操作,但会扼杀性能:)

【讨论】:

这是有道理的。我只是不确定 Django 的 ORM 是否内置了这种功能。我在文档中没有看到任何内容;想确保我没有遗漏任何东西。这是迄今为止我在使用 Django 的 ORM 时遇到的唯一限制。我发现使用它真的很愉快,在我使用它的环境中,我并不关心尖端性能——它非常适合我们,而且效率很高。【参考方案2】:

您需要的是一个递归函数,它遍历 OrganizationalUnit 关系树并获取每个 OrganizationalUnit 的相关 Checkout 数量。所以您的代码将如下所示:

def count_checkouts(ou):
   checkout_count = ou.checkouts.count()
   for kid in ou.children.all():
       checkout_count += count_checkouts(kid)
   return checkout_count

另外请注意,为了获得一些相关的结帐,我使用:

checkout_count = ou.checkouts.count()

安装于:

count = ou.checkouts.all().count()

我的变体更高效(参见http://docs.djangoproject.com/en/1.1/ref/models/querysets/#count)。

【讨论】:

【参考方案3】:

我认为最有效的计算方法是在写入时。您应该像这样修改 OrganizationalUnit:

class OrganizationalUnit(models.Model):
    name = models.CharField(max_length=100)
    parent = models.ForeignKey(
        'self',
        blank=True, null=True,
        related_name='children',
    )
    checkout_number = models.IntegerField(default=0)

创建将在写入时更新 OrganizationalUnit 及其父级的函数:

def pre_save_checkout(sender, instance, **kwargs):
    if isinstance(instance,Checkout) and instance.id and instance.department:
         substract_checkout(instance.department)

def post_save_checkout(sender, instance, **kwargs):
    if isinstance(instance,Checkout) and instance.department:
         add_checkout(instance.department)

def  substract_checkout(organizational_unit):
    organizational_unit.checkout_number-=1
    organizational_unit.save()
    if organizational_unit.parent:
        substract_checkout(organizational_unit.parent)

def  add_checkout(organizational_unit):
    organizational_unit.checkout_number+=1
    organizational_unit.save()
    if organizational_unit.parent:
        add_checkout(organizational_unit.parent)

现在您只需将这些函数连接到 pre_save、post_save 和 pre_delete 信号:

from django.db.models.signals import post_save, pre_save, pre_delete

pre_save.connect(pre_save_checkout, Checkout)
pre_delete.connect(pre_save_checkout, Checkout)
post_save.connect(post_save_checkout, Checkout)

应该可以的...

【讨论】:

以上是关于如何获得模型的相关对象和模型的子相关对象的总数?的主要内容,如果未能解决你的问题,请参考以下文章

Django 模型 - 相关对象验证

获取所有相关的 Django 模型对象

JavaScript宿主对象之BOM和DOM

SwiftUI之深入解析如何处理特定的数据和如何在视图中适配数据模型对象

Django使用“通过”关系表从模型中获取所有相关对象

Django 模型中的对象总数