Querysets
Introduction#
A Queryset
is fundamentally a list of objects derived from a Model
, by a compilation of database queries.
Simple queries on a standalone model
Here is a simple model that we will use to run a few test queries:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
Get a single model object where the id/pk is 4:
(If there are no items with the id of 4 or there are more than one, this will throw an exception.)
MyModel.objects.get(pk=4)
All model objects:
MyModel.objects.all()
Model objects that have flag
set to True
:
MyModel.objects.filter(flag=True)
Model objects with a model_num
greater than 25:
MyModel.objects.filter(model_num__gt=25)
Model objects with the name
of “Cheap Item” and flag
set to False
:
MyModel.objects.filter(name="Cheap Item", flag=False)
Models simple search name
for specific string(Case-sensitive):
MyModel.objects.filter(name__contains="ch")
Models simple search name
for specific string(Case-insensitive):
MyModel.objects.filter(name__icontains="ch")
Advanced queries with Q objects
Given the model:
class MyModel(models.Model):
name = models.CharField(max_length=10)
model_num = models.IntegerField()
flag = models.NullBooleanField(default=False)
We can use Q
objects to create AND
, OR
conditions in your lookup query. For example, say we want all objects that have flag=True
OR model_num>15
.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))
The above translates to WHERE flag=True OR model_num > 15
similarly for an AND you would do.
MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))
Q
objects also allow us to make NOT queries with the use of ~
. Let’s say we wanted to get all objects that have flag=False
AND model_num!=15
, we would do:
MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15))
If using Q objects and “normal” parameters in filter()
, then the Q
objects must come first. The following query searches for models with (flag
set to True
or a model number greater than 15
) and a name that starts with “H”.
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")
Note: Q
objects can be used with any lookup function that takes keyword arguments such as filter
, exclude
, get
. Make sure that when you use with get
that you will only return one object or the MultipleObjectsReturned
exception will be raised.
Reduce number of queries on ManyToManyField (n+1 issue)
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
Solution
Use prefetch_related
on ManyToManyField
if you know that you will need to access later a field which is a ManyToManyField
field.
# 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
can also be used on lookup fields :
# 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
However, once the queryset has been executed, the data fetched can’t be altered without hitting again the database. The following would execute extra queries for example:
# 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)
The following can be optimized using a Prefetch
object, introduced in 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
Reduce number of queries on ForeignKey field (n+1 issue)
Problem
Django querysets are evaluated in a lazy fashion. For example:
# 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
The code above causes django to query the database for the author of each book. This is inefficient, and it is better to only have a single query.
Solution
Use select_related
on ForeignKey
if you know that you will need to later access a ForeignKey
field.
# 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
can also be used on lookup fields:
# 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
Get SQL for Django queryset
The query
attribute on queryset gives you SQL equivalent syntax for your query.
>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"
Warning:
This output should only be used for debugging purposes. The generated query is not backend-specific. As such, the parameters aren’t quoted properly, leaving it vulnerable to SQL injection, and the query may not even be executable on your database backend.
Get first and last record from QuerySet
To get First object:
MyModel.objects.first()
To get last objects:
MyModel.objects.last()
Using Filter First object:
MyModel.objects.filter(name='simple').first()
Using Filter Last object:
MyModel.objects.filter(name='simple').last()
Advanced queries with F objects
An F() object represents the value of a model field or annotated column. It makes it possible to refer to model field values and perform database operations using them without actually having to pull them out of the database into Python memory. - F() expressions
It is appropriate to use F()
objects whenever you need to reference another field’s value in your query. By itself, F()
objects do not mean anything, and they cannot and should not be called outside of a queryset. They are used to reference a field’s value on the same queryset.
For example, given a model …
SomeModel(models.Model):
...
some_field = models.IntegerField()
… a user can query objects where the some_field
value is twice its id
by referencing the id
field’s value while filtering using F()
like this:
SomeModel.objects.filter(some_field=F('id') * 2)
F('id')
simply references the id
value for that same instance. Django uses it to create corresponding SQL statement. In this case something closely resembling this:
SELECT * FROM some_app_some_model
WHERE some_field = ((id * 2))
Without F()
expressions this would be accomplished with either raw SQL or filtering in Python (which reduces the performance especially when there are lots of objects).
References:
From F()
class definition:
An object capable of resolving references to existing query objects. - F source
Note: This example posted came from the answer listed above with consent from TinyInstance.