Maison  >  Article  >  développement back-end  >  Utiliser efficacement les QuerySets de Django

Utiliser efficacement les QuerySets de Django

巴扎黑
巴扎黑original
2017-04-05 15:05:571933parcourir

Le mappage objet-relationnel (ORM) simplifie l'interaction avec les bases de données SQL, mais il est également considéré comme inefficace et plus lent que le SQL brut.

Utiliser efficacement un ORM signifie comprendre comment il interroge la base de données. Dans cet article, je me concentrerai sur la façon d'utiliser efficacement le système Django ORM pour accéder à des ensembles de données de taille moyenne à grande.

L'ensemble de requêtes de Django est paresseux

Le jeu de requêtes de Django correspond à plusieurs enregistrements (lignes) de la base de données, filtrés via des requêtes facultatives. Par exemple, le code suivant obtiendra toutes les personnes nommées « Dave » dans la base de données :

person_set = Person.objects.filter(first_name="Dave")

Le code ci-dessus n'exécute aucune requête de base de données. Vous pouvez utiliser person_set, y ajouter des conditions de filtre ou le transmettre à une fonction, et ces opérations ne seront pas envoyées à la base de données. C'est exact car les requêtes de base de données sont l'un des facteurs qui affectent de manière significative les performances des applications Web.

Pour obtenir réellement des données de la base de données, vous devez parcourir l'ensemble de requêtes :

for person in person_set:
    print(person.last_name)

L'ensemble de requêtes de Django a un cache

Lorsque vous parcourez un ensemble de requêtes, tous les enregistrements correspondants sont récupérés de la base de données et convertis en modèle Django. C’est ce qu’on appelle l’évaluation. Ces modèles seront enregistrés dans le cache intégré de l'ensemble de requêtes, de sorte que si vous parcourez à nouveau l'ensemble de requêtes, vous n'aurez pas besoin d'exécuter à nouveau la même requête générale.

Par exemple, le code suivant n'exécutera une requête de base de données qu'une seule fois :

pet_set = Pet.objects.filter(species="Dog")
# The query is executed and cached.
for pet in pet_set:
    print(pet.first_name)
# The cache is used for subsequent iteration.
for pet in pet_set:
    print(pet.last_name)

L'instruction if déclenchera l'exécution du jeu de requêtes

La chose la plus utile à propos du cache de l'ensemble de requêtes est qu'il peut tester efficacement si l'ensemble de requêtes contient des données. Il ne sera parcouru que lorsqu'il y a des données :

restaurant_set = Restaurant.objects.filter(cuisine="Indian")
# `if`语句会触发queryset的执行。
if restaurant_set:
    # 遍历时用的是cache中的数据
    for restaurant in restaurant_set:
        print(restaurant.name)

. Si vous n'avez pas besoin de toutes les données, le cache de l'ensemble de requêtes peut poser problème

Parfois, vous souhaiterez peut-être simplement savoir si des données existent sans parcourir toutes les données. Dans ce cas, le simple fait d'utiliser une instruction if pour porter un jugement exécutera complètement l'ensemble de requêtes et mettra les données dans le cache, même si vous n'avez pas besoin des données !

city_set = City.objects.filter(name="Cambridge")
# `if`语句会执行queryset.。
if city_set:
    # 我们并不需要所有的数据,但是ORM仍然会获取所有记录!
    print("At least one city called Cambridge still stands!")

Pour éviter cela, vous pouvez utiliser la méthode exist() pour vérifier s'il y a des données :

tree_set = Tree.objects.filter(type="deciduous")
# `exists()`的检查可以避免数据放入queryset的cache。
if tree_set.exists():
    # 没有数据从数据库获取,从而节省了带宽和内存
    print("There are still hardwood trees in the world!")

Lorsque l'ensemble de requêtes est très volumineux, le cache deviendra un problème

Lors du traitement de milliers d’enregistrements, les charger en mémoire d’un seul coup est un gaspillage. Ce qui est pire, c'est qu'un énorme ensemble de requêtes peut verrouiller le processus système et amener votre programme au bord du crash.

Pour éviter de générer le cache de l'ensemble de requêtes lors du parcours des données, vous pouvez utiliser la méthode iterator() pour obtenir les données et les supprimer après leur traitement.

star_set = Star.objects.all()
# `iterator()`可以一次只从数据库获取少量数据,这样可以节省内存
for star in star_set.iterator():
    print(star.name)

Bien entendu, l'utilisation de la méthode iterator() pour empêcher la génération de cache signifie que les requêtes seront exécutées à plusieurs reprises lors du parcours du même ensemble de requêtes. Soyez donc prudent lorsque vous utilisez iterator() et assurez-vous que votre code n'exécute pas de requêtes à plusieurs reprises lorsque vous travaillez sur un grand ensemble de requêtes

Si l'ensemble de requêtes est volumineux, l'instruction if pose problème

Comme mentionné précédemment, le cache de l'ensemble de requêtes est puissant pour combiner les instructions if et for, ce qui permet des boucles conditionnelles sur un ensemble de requêtes. Toutefois, pour les ensembles de requêtes très volumineux, la mise en cache des ensembles de requêtes n’est pas adaptée.

La solution la plus simple consiste à utiliser exist() conjointement avec iterator() pour éviter d'utiliser le cache de l'ensemble de requêtes en utilisant deux requêtes de base de données.

molecule_set = Molecule.objects.all()
# One database query to test if any rows exist.
if molecule_set.exists():
    # Another database query to start fetching the rows in batches.
    for molecule in molecule_set.iterator():
        print(molecule.velocity)

Une solution plus compliquée consiste à utiliser la « méthode d'itération avancée » de Python pour examiner le premier élément de iterator() avant de démarrer la boucle, puis de décider s'il faut boucler.

atom_set = Atom.objects.all()
# One database query to start fetching the rows in batches.
atom_iterator = atom_set.iterator()
# Peek at the first item in the iterator.
try:
    first_atom = next(atom_iterator)
except StopIteration:
    # No rows were found, so do nothing.
    pass
else:
    # At least one row was found, so iterate over
    # all the rows, including the first one.
    from itertools import chain
    for atom in chain([first_atom], atom_set):
        print(atom.mass)

Empêcher une optimisation inappropriée

Le cache de Queryset est utilisé pour réduire les requêtes du programme sur la base de données. Dans des conditions normales d'utilisation, il garantira que la base de données ne sera interrogée qu'en cas de besoin.

Utilisez les méthodes exist() et iterator() pour optimiser l'utilisation de la mémoire par le programme. Cependant, comme ils ne génèrent pas de cache d'ensemble de requêtes, ils peuvent provoquer des requêtes de base de données supplémentaires.

Vous devez donc faire attention lors du codage. Si le programme commence à ralentir, vous devez voir où se trouvent les goulots d'étranglement dans le code et s'il existe de petites optimisations qui peuvent vous aider.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Article précédent:Comment utiliser Nginx + UWSGIArticle suivant:Comment utiliser Nginx + UWSGI