2009-08-18 10 views
2

je configuration des modèles Django de la manière suivante:requête Django pour un grand nombre de relations

modèle

A a un à-plusieurs au modèle B

chaque enregistrement A a entre 3 000 à 15 000 enregistrements dans B

Quelle est la meilleure façon de construire une requête qui récupérera l'enregistrement le plus récent (le plus grand PK) dans B qui correspond à un enregistrement dans A pour chaque enregistrement dans A? Est-ce quelque chose que je dois utiliser SQL pour remplacer l'ORM de Django?

Répondre

2

Créer une fonction d'aide pour extraire en toute sécurité le « top » élément de n'importe quel jeu de requête. Je l'utilise partout dans mes propres applications Django.

def top_or_none(queryset): 
    """Safely pulls off the top element in a queryset""" 
    # Extracts a single element collection w/ top item 
    result = queryset[0:1] 

    # Return that element or None if there weren't any matches 
    return result[0] if result else None 

Il utilise un peu d'un tour w/la slice operator to add a limit clause onto your SQL.

Maintenant, utilisez cette fonction où vous voulez obtenir l'élément 'haut' d'un ensemble de requêtes. Dans ce cas, vous voulez obtenir l'élément supérieur B pour une donnée A où les B sont classés par ordre décroissant pk, en tant que tel:

latest = top_or_none(B.objects.filter(a=my_a).order_by('-pk')) 

Il y a aussi récemment ajouté la fonction « Max » dans Django Aggregation qui pourrait vous aider obtenir le maximum de PC, mais je n'aime pas cette solution dans ce cas, car il ajoute de la complexité.

P.S. Je n'aime pas vraiment utiliser le champ 'pk' pour ce type de requête, car certains SGBDR ne garantissent pas que les paquets séquentiels sont identiques à l'ordre de création logique. Si j'ai une table dont je sais que je vais devoir faire une requête de cette manière, j'ai habituellement ma propre colonne 'creation' datetime que je peux utiliser pour passer commande au lieu de pk.

Modifier basé sur le commentaire:

Si vous préférez utiliser queryset [0], vous pouvez modifier le 'top_or_none' fonction ainsi:

def top_or_none(queryset): 
    """Safely pulls off the top element in a queryset""" 
    try: 
     return queryset[0] 
    except IndexError: 
     return None 

Je n'ont pas proposé ce départ parce que j'étais sous l'impression que QuerySet [0] reculerait l'ensemble des résultats, puis prendrait le 0ème élément. Apparemment, Django ajoute un 'LIMIT 1' dans ce scénario, donc c'est une alternative sûre à ma version de découpage.

Edit 2

Bien sûr, vous pouvez également profiter de gestionnaire connexes de Django construire ici et construire le queryset par votre « A » objet, selon vos préférences:

latest = top_or_none(my_a.b_set.order_by('-pk')) 
+0

Quelle est la différence entre résultat = jeu de queues [0: 1] et résultat = jeu de queues [0]? – hekevintran

+0

queryset [0: 1] renvoie une liste vide lorsqu'il n'y a pas d'éléments correspondants, tandis que queryset [0] lance une erreur IndexError. –

+0

Merci pour la réponse! – hekevintran

0

Je ne pense pas que Django ORM puisse le faire (mais j'ai été agréablement surpris avant ...). S'il y a un nombre raisonnable d'enregistrements A (ou si vous paginez), j'ajouterais simplement une méthode à un modèle qui retournerait cet enregistrement B «le plus récent». Si vous voulez obtenir beaucoup d'enregistrements A, chacun avec son propre B, je passerais à SQL.

remeber que peu importe la route que vous prenez, vous aurez besoin d'un indice composite approprié sur la table B, en ajoutant peut-être un order_by=('a_fk','-id') à la Meta sous-classe

Questions connexes