如何获取模型的相关对象和模型的子项相关对象的总计数?

时间:2010-01-27 22:07:38

标签: python django many-to-many models django-select-related

在Django,我有一个Checkout模型,这是一个检查设备的人的票。我还有一个Checkout模型与(通过ForeignKey)相关的OrganizationalUnit模型,因为结账时的人属于我们校园的OrganizationalUnit。

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',
)

我想了解与某个OrganizationalUnit及其所有孩子相关的Checkout。我知道如何获得与OU相关的所有结账的计数。

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

但是,我如何计算这个OU的孩子及其子女的结账?我是否使用某种迭代循环?


编辑:我想我还是不能完全围绕while命令来做这件事。组织单位可以像用户想要嵌套它们一样深,但是现在,它在数据库中的最大值是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()

...这是完全废话。它需要一段时间才能运行,因为它几乎遍历了数据库的主要部分。救命! (我今天似乎无法很好地思考。)

3 个答案:

答案 0 :(得分:3)

您需要的是一个递归函数,它遍历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)。

答案 1 :(得分: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)

应该这样做......

答案 2 :(得分:0)

我不确定SQL如何在这个上执行,但你想要做的就是你所解释的。

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

ORM为您带来了对SQL的动态操作,但会破坏性能:)