Django - limitation des résultats de requête

200

Je veux prendre les 10 dernières instances d'un modèle et avoir ce code:

 Model.objects.all().order_by('-id')[:10]

Est-il vrai que vous devez d'abord prendre toutes les instances, puis n'en prendre que 10 dernières? Existe-t-il une méthode plus efficace?

krzyhub
la source

Réponses:

304

Les ensembles de requêtes Django sont paresseux. Cela signifie qu'une requête atteindra la base de données uniquement lorsque vous demanderez spécifiquement le résultat.

Donc, jusqu'à ce que vous imprimiez ou utilisiez réellement le résultat d'une requête, vous pouvez filtrer davantage sans accès à la base de données.

Comme vous pouvez le voir ci-dessous, votre code n'exécute qu'une seule requête SQL pour récupérer uniquement les 10 derniers éléments.

In [19]: import logging                                 
In [20]: l = logging.getLogger('django.db.backends')    
In [21]: l.setLevel(logging.DEBUG)                      
In [22]: l.addHandler(logging.StreamHandler())      
In [23]: User.objects.all().order_by('-id')[:10]          
(0.000) SELECT "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined" FROM "auth_user" ORDER BY "auth_user"."id" DESC LIMIT 10; args=()
Out[23]: [<User: hamdi>]
hamdiakoguz
la source
J'ai essayé cela sur mongoDB et il est dit que SELECT n'est pas pris en charge. Comment faire cela sur mongoDB?
winux
@winux Étant donné que cela est spécifique à Django, il semble que vous deviez peut-être envisager de configurer Django pour qu'il fonctionne spécifiquement avec les bases de données de type Mongo / NoSQL. Ce n'est pas une configuration typique dans mon expérience, en ce qui concerne la configuration standard de Django ORM.
lâche anonyme
38

En fait, je pense que le LIMIT 10serait délivré à la base de données afin que le découpage ne se produise pas en Python mais dans la base de données.

Voir limitation-querysets pour plus d'informations.

Davor Lucic
la source
Notez que cela ne fonctionnera pas pour les ensembles de requêtes qui nécessitent également un filtrage, car vous ne pouvez pas filtrer après le découpage.
Mike 'Pomax' Kamermans
2
Donc, filtrez d'abord que coupez-le. Merci Davor pour le lien!
Vyachez
13

Il semble que la solution de la question ne fonctionne plus avec Django 1.7 et génère une erreur: "Impossible de réorganiser une requête une fois qu'une tranche a été prise"

Selon la documentation https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets forçant le paramètre «step» de la syntaxe de tranche Python évalue la requête. Cela fonctionne de cette façon:

Model.objects.all().order_by('-id')[:10:1]

Je me demande toujours si la limite est exécutée dans des tranches SQL ou Python, le tableau de résultats entier retourné. Il ne sert à rien de récupérer d'énormes listes dans la mémoire de l'application.

Nikolay Grischenko
la source
Même cette solution ne fonctionne pas avec django> = 1.8 testé.
sonus21
3

Oui. Si vous souhaitez récupérer un sous-ensemble limité d'objets, vous pouvez le faire avec le code ci-dessous:

Exemple:

obj=emp.objects.all()[0:10]

Le début 0 est facultatif, donc

obj=emp.objects.all()[:10]

Le code ci-dessus renvoie les 10 premières instances.

patel shahrukh
la source
1

Comme ajout et observation aux autres réponses utiles, il convient de noter qu'en réalité, le fait [:10]de trancher renvoie les 10 premiers éléments de la liste , pas les 10 derniers ...

Pour obtenir les 10 derniers, vous devez faire à la [-10:]place (voir ici ). Cela vous évitera d'utiliser order_by('-id')avec le -pour inverser les éléments.

DarkCygnus
la source
1
J'ai essayé cela et j'ai obtenu "L'indexation négative n'est pas prise en charge."
bparker
@DarkCygnus Product.objects.filter(~Q(price=0))[-5:]me cause la même erreur: "L'indexation négative n'est pas prise en charge."
bersam
Cela ne fonctionne pas dans django sur un ensemble de requêtes: code.djangoproject.com/ticket/13089 Si vous convertissez l'ensemble de requêtes en liste, cela fonctionnera.
valem