2010-01-27 3 views
3

Dans Django, j'ai un modèle Checkout, qui est un ticket pour quelqu'un qui vérifie l'équipement. J'ai aussi un modèle OrganizationalUnit auquel se rapporte le modèle Checkout (via ForeignKey), car la personne à la caisse appartient à un OrganizationalUnit sur notre campus. Le composant OrganizationalUnit a une relation de soi, de sorte que plusieurs unités d'organisation peuvent être les enfants d'une unité d'organisation donnée, et ces enfants peuvent avoir des enfants, et ainsi de suite. Voici les modèles, un peu simplifiés.Comment puis-je obtenir le nombre total d'objets associés d'un modèle et des objets associés aux enfants du modèle?

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

Je souhaite connaître le nombre de Checkouts associés à un certain OrganizationalUnit et à tous ses enfants. Je sais comment obtenir le compte de toutes les caisses qui sont liées à une OU. Mais comment faire pour que ce compte reflète les retraits des enfants de cette unité d'organisation et de leurs enfants? Est-ce que j'utilise une sorte de boucle itérative?


EDIT: Je suppose que je ne peux toujours pas tout à fait envelopper la tête autour de la commande while pour le faire. Les unités organisationnelles peuvent aller aussi loin que l'utilisateur veut les imbriquer, mais à l'heure actuelle, le maximum est de 5 profonds. J'ai écrit ceci ...

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() 

... ce qui est total de la merde. Et il faut un certain temps pour s'exécuter car il traverse à peu près une partie importante de la base de données. Aidez-moi! (Je ne peux pas sembler penser très bien aujourd'hui.)

Répondre

3

Je pense que le moyen le plus efficace de calculer cela est au moment de l'écriture.Vous devez modifier OrganizationalUnit comme ceci:

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) 

créer les fonctions qui mettent à jour le OrganizationalUnit et ses parents, au moment de l'écriture:

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) 

maintenant tout ce dont vous avez besoin est de connecter ces fonctions à l'pre_save, post_save et signaux 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) 

cela devrait le faire ...

0

Je ne suis pas sûr comment SQL exécute sur celui-ci mais ce que vous voulez faire est exactement ce que vous avez expliqué.

Obtenez tous les OU et ses parents avec la boucle While, puis comptez Checkouts et les additionner.

ORM vous apporte des opérations dynamiques sur SQL mais tuer les performances :)

+0

qui fait sens. Je ne savais pas si l'ORM de Django avait ce genre de fonctionnalités intégrées. Je n'ai rien vu dans la documentation; Je voulais m'assurer que je ne manquais de rien. C'est la seule limite que j'ai rencontrée avec l'ORM de Django. J'ai trouvé cela très agréable à utiliser, et dans le contexte dans lequel je travaille, je ne suis pas concerné par les performances de pointe - cela nous convient très bien, et c'est plutôt efficace. –

3

Qu'est-ce que vous avez besoin est une fonction récursive qui traversent OrganizationalUnit relation arbre et obtient nombre de Checkouts connexes pour chaque OrganizationalUnit. Donc, votre code ressemblera à ceci:

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

Notez également que pour obtenir un certain nombre de checkouts connexes J'utilise:

checkout_count = ou.checkouts.count() 

insted de:

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

Ma variante est plus efficace (voir http://docs.djangoproject.com/en/1.1/ref/models/querysets/#count).

Questions connexes