Sök…


Introduktion

En Queryset är i grunden en lista med objekt som härrör från en Model , genom en sammanställning av databasfrågor.

Enkla frågor på en fristående modell

Här är en enkel modell som vi kommer att använda för att köra några testfrågor:

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

Skaffa ett enda modellobjekt där id / pk är 4:
(Om det inte finns några artiklar med ID: n eller om det finns fler än ett, kommer detta att göra ett undantag.)

MyModel.objects.get(pk=4)

Alla modellobjekt:

MyModel.objects.all()

Modellobjekt som har flag inställd på True :

MyModel.objects.filter(flag=True)

model_num med model_num större än 25:

MyModel.objects.filter(model_num__gt=25)

Modellobjekt med name "Billigt objekt" och flag inställt på False :

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

Modeller enkel sökning name för specifik sträng (skiftlägeskänsligt):

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

Modeller enkel sökning name för specifik sträng (Case-okänslig):

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

Avancerade frågor med Q-objekt

Med tanke på modellen:

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

Vi kan använda Q objekt för att skapa AND , OR villkor i din sökfråga. Till exempel, säg att vi vill ha alla objekt som har flag=True ELLER model_num>15 .

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

Ovanstående översätter till WHERE flag=True OR model_num > 15 liknande sätt för en OCH du skulle göra.

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

Q objekt tillåter oss också att INTE göra frågor med ~ . Låt oss säga att vi ville få alla objekt som har flag=False OCH model_num!=15 , vi skulle göra:

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

Om du använder Q-objekt och "normala" parametrar i filter() , måste Q objekten komma först . Följande fråga söker efter modeller med ( flag inställd på True eller ett modellnummer större än 15 ) och ett namn som börjar med "H".

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

Obs: Q objekt kan användas med alla uppslagningsfunktioner som tar nyckelordargument som filter , exclude , get . Se till att när du använder med get att du bara returnerar ett objekt eller att MultipleObjectsReturned undantaget kommer att höjas.

Minska antalet frågor på ManyToManyField (n + 1 nummer)

Problem

# 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

Lösning

Använd prefetch_relatedManyToManyField om du vet att du senare måste komma åt ett fält som är ett ManyToManyField fält.

# 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 kan också användas i uppslagningsfält:

# 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

När emellertid queryset har utförts kan data som hämtas dock inte ändras utan att träffa databasen igen. Följande skulle utföra extra frågor till exempel:

 # 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)

Följande kan optimeras med ett Prefetch objekt, introducerat i 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

Minska antalet frågor på fältet ForeignKey (n + 1 nummer)

Problem

Django-frågeställningar utvärderas på ett lat sätt. Till exempel:

# 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

Koden ovan får django att fråga databasen för författaren till varje bok. Detta är ineffektivt, och det är bättre att bara ha en enda fråga.

Lösning

Använd select_relatedForeignKey om du vet att du senare måste komma åt ett ForeignKey fält.

# 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 kan också användas i sökfält:

# 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

Skaffa SQL för Django-queryset

Den query attribut på queryset ger SQL motsvarande syntax för din fråga.

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

Varning:

Denna utgång ska endast användas för felsökning. Den genererade frågan är inte backendspecifik. Som sådana citeras inte parametrarna ordentligt, vilket lämnar den sårbar för SQL-injektion, och frågan kanske inte ens är körbar i din databasbackend.

Få första och sista posten från QuerySet

För att få första objektet:

MyModel.objects.first()

För att få sista föremål:

MyModel.objects.last()

Använda filterets första objekt:

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

Använda filter förra objektet:

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

Avancerade frågor med F-objekt

Ett F () -objekt representerar värdet på ett modellfält eller en kommenterad kolumn. Det gör det möjligt att hänvisa till modellfältvärden och utföra databasåtgärder med hjälp av dem utan att behöva dra dem ut ur databasen i Python-minnet. - F () uttryck

Det är lämpligt att använda F() -objekt när du behöver referera till ett annat fältvärde i din fråga. I sig själv betyder inte F() -objekt något, och de kan inte och bör inte kallas utanför en frågeställning. De används för att referera till ett fältvärde på samma frågeställning.

Till exempel, med en modell ...

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

... en användare kan fråga objekt där some_field är två gånger dess id genom att hänvisa till id fältets värde medan du filtrerar med F() så här:

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

F('id') hänvisar helt enkelt till id värdet för samma instans. Django använder den för att skapa motsvarande SQL-sats. I detta fall något som liknar detta:

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

Utan F() -uttryck skulle detta uppnås med antingen rå SQL eller filtrering i Python (vilket minskar prestandan särskilt när det finns massor av objekt).


referenser:

Från F() klassdefinition:

Ett objekt som kan lösa referenser till befintliga frågeställningar. - F-källa

Obs: Detta publicerade exempel kom från svaret som listas ovan med samtycke från TinyInstance.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow