在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()
...这是完全废话。它需要一段时间才能运行,因为它几乎遍历了数据库的主要部分。救命! (我今天似乎无法很好地思考。)
答案 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的动态操作,但会破坏性能:)