如何获得模型的相关对象和模型的子相关对象的总数?
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)
应该可以的...
【讨论】:
以上是关于如何获得模型的相关对象和模型的子相关对象的总数?的主要内容,如果未能解决你的问题,请参考以下文章