Recherche…


Introduction

Un Queryset est fondamentalement une liste d'objets dérivés d'un Model , par une compilation de requêtes de base de données.

Requêtes simples sur un modèle autonome

Voici un modèle simple que nous utiliserons pour exécuter quelques requêtes de test:

class MyModel(models.Model):
    name = models.CharField(max_length=10)
    model_num = models.IntegerField()
    flag = models.NullBooleanField(default=False)

Obtenez un objet de modèle unique où l'id / pk est 4:
(S'il n'y a pas d'éléments avec l'id de 4 ou s'il y en a plus d'un, cela lancera une exception.)

MyModel.objects.get(pk=4)

Tous les objets du modèle:

MyModel.objects.all()

Les objets de modèle dont l' flag défini sur True :

MyModel.objects.filter(flag=True)

Modéliser des objets avec un model_num supérieur à 25:

MyModel.objects.filter(model_num__gt=25)

Les objets de modèle portant le name "Élément bon marché" et le flag défini sur False :

MyModel.objects.filter(name="Cheap Item", flag=False)

Modélise un name recherche simple pour une chaîne spécifique (sensible à la casse):

MyModel.objects.filter(name__contains="ch")

Modèles Recherche simple name de chaîne spécifique (insensible à la casse):

MyModel.objects.filter(name__icontains="ch")

Requêtes avancées avec des objets Q

Vu le modèle:

class MyModel(models.Model):
    name = models.CharField(max_length=10)
    model_num = models.IntegerField()
    flag = models.NullBooleanField(default=False)

Nous pouvons utiliser des objets Q pour créer des conditions AND , OR dans votre requête de recherche. Par exemple, supposons que tous les objets ayant flag=True OR model_num>15 .

from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))

Ce qui précède se traduit par WHERE flag=True OR model_num > 15 même pour un ET que vous feriez.

MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))

Q objets Q nous permettent également de faire des requêtes NOT avec l'utilisation de ~ . Disons que nous voulions obtenir tous les objets qui ont flag=False AND model_num!=15 , nous ferions:

MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15)) 

Si vous utilisez des objets Q et des paramètres "normaux" dans filter() , les objets Q doivent être placés en premier . La requête suivante recherche les modèles avec ( flag défini sur True ou un numéro de modèle supérieur à 15 ) et un nom commençant par "H".

from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")

Remarque: les objets Q peuvent être utilisés avec toute fonction de recherche prenant en compte les arguments de mots clés tels que filter , exclude , get . Assurez-vous que lorsque vous utilisez avec get , vous ne retournerez qu'un objet ou que l'exception MultipleObjectsReturned sera déclenchée.

Réduire le nombre de requêtes sur ManyToManyField (problème n + 1)

Problème

# models.py:
class Library(models.Model):
    name = models.CharField(max_length=100)
    books = models.ManyToManyField(Book)

class Book(models.Model):
    title = models.CharField(max_length=100)
# views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.all()

    # Query the database on each iteration (len(author) times)
    # if there is 100 librairies, there will have 100 queries plus the initial query
    for library in libraries:
        books = library.books.all()
        books[0].title
        # ...

    # total : 101 queries

Solution

Utilisez prefetch_related sur ManyToManyField si vous savez que vous devrez accéder ultérieurement à un champ qui est un champ ManyToManyField .

# views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related('books').all()
    
    # Does not query the database again, since `books` is pre-populated
    for library in libraries:
        books = library.books.all()
        books[0].title
        # ...

    # total : 2 queries - 1 for libraries, 1 for books

prefetch_related peut également être utilisé sur les champs de recherche:

# models.py:
class User(models.Model):
    name = models.CharField(max_length=100)

class Library(models.Model):
    name = models.CharField(max_length=100)
    books = models.ManyToManyField(Book)

class Book(models.Model):
    title = models.CharField(max_length=100)
    readers = models.ManyToManyField(User)
 # views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related('books', 'books__readers').all()
    
    # Does not query the database again, since `books` and `readers` is pre-populated
    for library in libraries:
        for book in library.books.all():
            for user in book.readers.all():
                user.name
                # ...

    # total : 3 queries - 1 for libraries, 1 for books, 1 for readers

Cependant, une fois que le jeu de requête a été exécuté, les données extraites ne peuvent pas être modifiées sans toucher à nouveau la base de données. Les éléments suivants exécuteraient des requêtes supplémentaires, par exemple:

 # views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related('books').all()
    for library in libraries:
        for book in library.books.filter(title__contains="Django"):
            print(book.name)

Les éléments suivants peuvent être optimisés en utilisant un objet Prefetch , introduit dans Django 1.7:

from django.db.models import Prefetch
# views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related(
        Prefetch('books', queryset=Book.objects.filter(title__contains="Django")
    ).all()
    for library in libraries:
        for book in library.books.all():
            print(book.name)  # Will print only books containing Django for each library

Réduire le nombre de requêtes sur le champ ForeignKey (problème n + 1)

Problème

Les groupes de requêtes Django sont évalués paresseusement. Par exemple:

# models.py:
class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author, related_name='books')
    title = models.CharField(max_length=100)
# views.py
def myview(request):
    # Query the database
    books = Book.objects.all()

    for book in books:
        # Query the database on each iteration to get author (len(books) times)
        # if there is 100 books, there will have 100 queries plus the initial query
        book.author
        # ...

    # total : 101 queries

Le code ci-dessus fait que django interroge la base de données pour trouver l'auteur de chaque livre. Ceci est inefficace et il est préférable de ne disposer que d'une seule requête.

Solution

Utilisez select_related sur ForeignKey si vous savez que vous devrez ultérieurement accéder à un champ ForeignKey .

# views.py
def myview(request):
    # Query the database.
    books = Books.objects.select_related('author').all()
    
    for book in books:
        # Does not query the database again, since `author` is pre-populated
        book.author
        # ...

    # total : 1 query

select_related peut également être utilisé dans les champs de recherche:

# models.py:
class AuthorProfile(models.Model):
    city = models.CharField(max_length=100)

class Author(models.Model):
    name = models.CharField(max_length=100)
    profile = models.OneToOneField(AuthorProfile)

class Book(models.Model):
    author = models.ForeignKey(Author, related_name='books')
    title = models.CharField(max_length=100)    
# views.py
def myview(request):
    books = Book.objects.select_related('author')\
                        .select_related('author__profile').all()
   
    for book in books:
        # Does not query database
        book.author.name
        # or
        book.author.profile.city
        # ...

    # total : 1 query

Obtenir le jeu de requête SQL pour Django

L'attribut query sur queryset vous donne une syntaxe équivalente SQL pour votre requête.

>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"

Attention:

Cette sortie ne doit être utilisée qu'à des fins de débogage. La requête générée n'est pas spécifique au backend. En tant que tels, les paramètres ne sont pas correctement cités, ce qui les rend vulnérables à l'injection SQL, et la requête peut même ne pas être exécutable sur votre backend de base de données.

Obtenir le premier et le dernier enregistrement de QuerySet

Pour obtenir le premier objet:

MyModel.objects.first()

Pour obtenir les derniers objets:

MyModel.objects.last()

Utilisation du filtre Premier objet:

MyModel.objects.filter(name='simple').first()

Utilisation du filtre Dernier objet:

MyModel.objects.filter(name='simple').last()

Requêtes avancées avec des objets F

Un objet F () représente la valeur d'un champ de modèle ou d'une colonne annotée. Il permet de faire référence aux valeurs de champs de modèle et d'effectuer des opérations de base de données en les utilisant sans avoir à les extraire de la base de données en mémoire Python. - Expressions F ()

Il est approprié d'utiliser des objets F() chaque fois que vous devez référencer la valeur d'un autre champ dans votre requête. En soi, les objets F() ne veulent rien dire et ils ne peuvent et ne doivent pas être appelés en dehors d'un jeu de requête. Ils sont utilisés pour référencer la valeur d'un champ sur le même jeu de requête.

Par exemple, donné un modèle ...

SomeModel(models.Model):
    ...
    some_field = models.IntegerField()

... un utilisateur peut interroger des objets dont la valeur some_field correspond à deux fois son id en référençant la valeur du champ id lors du filtrage à l'aide de F() comme ceci:

SomeModel.objects.filter(some_field=F('id') * 2)

F('id') référence simplement la valeur de l' id pour cette même instance. Django l'utilise pour créer l'instruction SQL correspondante. Dans ce cas, quelque chose qui ressemble beaucoup à ceci:

SELECT * FROM some_app_some_model 
WHERE some_field = ((id * 2))

Sans les expressions F() , cela se ferait avec du SQL brut ou du filtrage en Python (ce qui réduit les performances, surtout quand il y a beaucoup d'objets).


Les références:

De la définition de la classe F() :

Un objet capable de résoudre les références aux objets de requête existants. - source F

Remarque: Cet exemple publié provient de la réponse répertoriée ci-dessus avec le consentement de TinyInstance.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow