Django
Querysets
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:
- Los filtros pueden hacer referencia a los campos en el modelo.
- Expresiones f
- Respuesta de TinyInstance
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.