Как я могу получить общее количество связанных объектов модели и дочерних объектов модели? - PullRequest
3 голосов
/ 28 января 2010

В Django у меня есть модель Checkout, которая является билетом для тех, кто проверяет оборудование. У меня также есть модель OrganizationalUnit, к которой относится модель Checkout (через ForeignKey), так как человек на кассе принадлежит к 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',
)

Я хочу получить количество проверок, связанных с определенным организационным подразделением и всеми его дочерними элементами. Я знаю, как получить количество всех проверок, связанных с подразделением.

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

Но как мне сделать так, чтобы этот счет отражал проверки детей этого подразделения и их детей? Я использую какой-то итерационный цикл?


РЕДАКТИРОВАТЬ : Полагаю, я все еще не могу обернуть голову вокруг команды 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 ]

3 голосов
/ 29 января 2010

Вам нужна рекурсивная функция, которая пересекает дерево отношений OrganizationalUnit и получает количество связанных проверок для каждого OrganizationalUnit. Итак, ваш код будет выглядеть так:

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 голосов
/ 06 февраля 2010

Я думаю, что наиболее эффективный способ расчета это во время записи. Вы должны изменить 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)

Это должно сделать это ...

0 голосов
/ 28 января 2010

Я не уверен, как SQL работает на этом, но то, что вы хотите сделать, это именно то, что вы объяснили.

Получите все OU и его родителей с помощью цикла While, а затем посчитайте Checkouts и суммируйте их.

ORM предоставляет вам динамические операции над SQL, но снижает производительность:)

...