Buscar..


Introducción

Un Queryset es fundamentalmente una lista de objetos derivados de un Model , por una compilación de consultas de base de datos.

Consultas simples en un modelo independiente.

Aquí hay un modelo simple que usaremos para ejecutar algunas consultas de prueba:

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

Consigue un objeto modelo único donde el id / pk es 4:
(Si no hay elementos con el ID de 4 o hay más de uno, esto generará una excepción).

MyModel.objects.get(pk=4)

Todos los objetos modelo:

MyModel.objects.all()

Objetos de modelo que tienen el flag establecido en True :

MyModel.objects.filter(flag=True)

Objetos de modelo con un model_num mayor que 25:

MyModel.objects.filter(model_num__gt=25)

Objetos de modelo con el name de "Artículo barato" y flag establecida en False :

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

Modelos de name búsqueda simple para una cadena específica (distingue entre mayúsculas y minúsculas)

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

Modelos de búsqueda simple de name para una cadena específica (no distingue mayúsculas y minúsculas):

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

Consultas avanzadas con objetos Q

Dado el modelo:

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

Podemos usar objetos Q para crear condiciones AND , OR en su consulta de búsqueda. Por ejemplo, supongamos que queremos que todos los objetos que tienen el flag=True O model_num>15 .

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

Lo anterior se traduce a WHERE flag=True OR model_num > 15 manera similar para un AND que haría.

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

Q objetos Q también nos permiten hacer consultas NO con el uso de ~ . Digamos que queríamos obtener todos los objetos que tienen flag=False AND model_num!=15 , haríamos:

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

Si se usan objetos Q y parámetros "normales" en el filter() , entonces los objetos Q deben aparecer primero . La siguiente consulta busca modelos con ( flag establecida en True o un número de modelo mayor que 15 ) y un nombre que comience con "H".

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

Nota: los objetos Q se pueden usar con cualquier función de búsqueda que tome argumentos de palabras clave como filter , exclude , get . Asegúrese de que cuando use con get solo devolverá un objeto o se generará la excepción MultipleObjectsReturned .

Reducir el número de consultas en ManyToManyField (problema n + 1)

Problema

# 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

Solución

Use prefetch_related en ManyToManyField si sabe que necesitará acceder más tarde a un campo que es un campo 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 también se puede utilizar en los campos de búsqueda:

# 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

Sin embargo, una vez que se ha ejecutado el queryset, los datos obtenidos no pueden alterarse sin volver a golpear la base de datos. Lo siguiente ejecutaría consultas extra por ejemplo:

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

Lo siguiente se puede optimizar utilizando un objeto Prefetch , introducido en 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

Reducir el número de consultas en el campo ForeignKey (problema n + 1)

Problema

Los querysets de Django se evalúan de manera perezosa. Por ejemplo:

# 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

El código anterior hace que django consulte la base de datos del autor de cada libro. Esto es ineficiente, y es mejor tener solo una consulta.

Solución

Utilice select_related en ForeignKey si sabe que necesitará acceder más tarde a un campo 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 también se puede utilizar en los campos de búsqueda:

# 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

Obtener SQL para Django Queryset

El atributo de query en queryset le proporciona una sintaxis equivalente a SQL para su consulta.

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

Advertencia:

Esta salida solo se debe utilizar para fines de depuración. La consulta generada no es específica del backend. Como tales, los parámetros no se citan correctamente, lo que lo hace vulnerable a la inyección de SQL, y es posible que la consulta ni siquiera sea ejecutable en su base de datos.

Obtener el primer y último registro de QuerySet

Para obtener el primer objeto:

MyModel.objects.first()

Para obtener los últimos objetos:

MyModel.objects.last()

Usando el objeto Filter First:

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

Usando Filter Last objeto:

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

Consultas avanzadas con objetos F

Un objeto F () representa el valor de un campo de modelo o columna anotada. Permite hacer referencia a los valores de campo del modelo y realizar operaciones de base de datos usándolos sin tener que sacarlos de la base de datos a la memoria de Python. - F () expresiones

Es apropiado usar objetos F() siempre que necesite hacer referencia al valor de otro campo en su consulta. Por sí mismos, los objetos F() no significan nada, y no pueden y no deben llamarse fuera de un queryset. Se utilizan para hacer referencia al valor de un campo en el mismo queryset.

Por ejemplo, dado un modelo ...

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

... un usuario puede consultar objetos donde el valor de some_field es el doble de su id haciendo referencia al valor del campo de id mientras filtra usando F() esta manera:

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

F('id') simplemente hace referencia al valor de id para esa misma instancia. Django lo usa para crear la declaración SQL correspondiente. En este caso algo muy parecido a esto:

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

Sin las expresiones F() , esto se lograría con SQL sin procesar o filtrado en Python (lo que reduce el rendimiento, especialmente cuando hay muchos objetos).


Referencias:

De la definición de la clase F() :

Un objeto capaz de resolver referencias a objetos de consulta existentes. - fuente F

Nota: este ejemplo publicado provino de la respuesta que aparece arriba con el consentimiento de TinyInstance.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow