2009-06-25 7 views
27

J'ai un QuerySet, appelons-le qs, qui est trié par un attribut qui n'est pas pertinent pour ce problème. Ensuite, j'ai un objet, appelons-le obj. Maintenant, je voudrais savoir à quel index obj a qs, comme efficacement que possible. Je sais que je pourrais utiliser .index() de Python ou peut-être boucle à travers qs en comparant chaque objet à obj, mais quelle est la meilleure façon de faire cela? Je suis à la recherche de haute performance et c'est mon seul critère.Récupère l'index d'un élément dans un jeu de requête

Utilisation de Python 2.6.2 avec Django 1.0.2 sous Windows.

+0

Juste pour une mise à jour, tout d'abord, l'ensemble de requêtes n'est pas ordonné. Ainsi, l'indice peut varier pour différentes itérations. Vous devez faire 'order_by' sur n'importe quel champ et ensuite la réponse de Vinay vous aidera si vous avez juste besoin de l'index. – Babu

+0

Si nous avons 10 millions d'enregistrements, c'est une opération difficile pour la base de données. – alexche8

Répondre

13

Les QuerySets dans Django sont en fait des générateurs, pas des listes (pour plus de détails, voir Django documentation on QuerySets).
En tant que tel, il n'y a pas de raccourci pour obtenir l'index d'un élément, et je pense qu'une simple itération est la meilleure façon de le faire.

Pour le démarreur, je mettrais en œuvre votre exigence de la manière la plus simple possible (comme l'itération); Si vous avez vraiment des problèmes de performance, alors j'utiliserais une approche différente, comme construire un jeu de requête avec une plus petite quantité de champs, ou peu importe.
Dans tous les cas, l'idée est de laisser ces astuces le plus tard possible, quand on sait que vous en avez vraiment besoin.
Mise à jour: Vous pouvez utiliser directement une instruction SQL pour obtenir le numéro de rown (quelque chose se trouve cependant, ORM de Django ne le supporte pas nativement et vous devez utiliser une requête SQL brute (voir documentation). être la meilleure option, mais encore une fois - que si vous voyez vraiment un vrai problème de performance

+0

Je vois. Cela fait un moment que j'ai touché SQL pour la dernière fois, mais j'ai pensé que ce serait possible en langage SQL et donc possible en utilisant l'API QuerySet de Django. –

+0

Oui, cela pourrait être une option. Je l'ai ajouté aux solutions possibles. –

50

Compact et probablement le plus efficace.

for index, item in enumerate(your_queryset): 
    ... 
11

en supposant dans le but d'illustration que vos modèles sont standard avec un primaire touche id, puis évalue

list(qs.values_list('id', flat=True)).index(obj.id) 

trouverez l'index de obj dans qs. Alors que l'utilisation de list évalue le jeu de requête, il n'évalue pas le jeu de requête original mais un jeu de requête dérivé. Cette évaluation exécute une requête SQL pour obtenir les champs d'identification uniquement, sans perdre de temps à extraire d'autres champs.

+1

Cela peut être plus efficace ou il peut être moins efficace que d'évaluer simplement le jeu de requête d'origine, selon que vous alliez le faire de toute façon plus tard (puisque les jeux de requête cache leurs résultats). –

+1

Il y a un problème avec cette méthode. qs.values_list ('id', flat = True) retourne des valeurs différentes à chaque étape. Vous obtenez un index différent avec la même requête. – Virako

26

Si vous voulez juste savoir où vous vous opposez assis parmi tous les autres (par exemple, lorsque le rang déterminant), vous pouvez le faire rapidement en comptant les objets avant:

index = MyModel.objects.filter(sortField__lt = myObject.sortField).count() 
+4

Dans ce cas, vous voudriez aussi que 'sortField' soit unique, sinon vous obtiendriez la position du premier élément avec' sortField' égal à 'myObject.sortField'. – naktinis

+0

Ne devrait-il pas être '.count() - 1'? Par exemple, s'il y a 1 objet dans le jeu de requête, l'index est 0. –

1

Vous pouvez le faire en utilisant queryset.extra(…) et certains SQL brut comme suit:

queryset = queryset.order_by("id") 
record500 = queryset[500] 

numbered_qs = queryset.extra(select={ 
    'queryset_row_number': 'ROW_NUMBER() OVER (ORDER BY "id")' 
}) 

from django.db import connection 
cursor = connection.cursor() 
cursor.execute(
    "WITH OrderedQueryset AS (" + str(numbered_qs.query) + ") " 
    "SELECT queryset_row_number FROM OrderedQueryset WHERE id = %s", 
    [record500.id] 
    ) 
index = cursor.fetchall()[0][0] 

index == 501 # because row_number() is 1 indexed not 0 indexed 
+0

oui, mais si j'ai des ID comme 1,4,5,6 ....... 500? – alexche8

+0

@ alexche8 il fonctionne assez bien pour moi sur les ensembles de résultats dans les 10 de milliers (que je pagine ensuite) – Jiaaro

Questions connexes