Introduction
Once you’ve created your models, Django provides a powerful QuerySet API for retrieving objects from the database. The QuerySet API is defined in/django/db/models/query.py.
Retrieving Objects
Get All Objects
Retrieve all objects from a model:from myapp.models import Article
# Get all articles
articles = Article.objects.all()
QuerySets are lazy - they don’t hit the database until you evaluate them (iterate, slice, call
list(), etc.).Filtering Objects
Usefilter() to retrieve objects matching certain criteria:
# Get published articles
published = Article.objects.filter(is_published=True)
# Multiple conditions (AND)
articles = Article.objects.filter(
is_published=True,
created_at__year=2024
)
# Chain filters (equivalent to above)
articles = Article.objects.filter(
is_published=True
).filter(
created_at__year=2024
)
Excluding Objects
Useexclude() to filter out objects:
# Get all articles except drafts
articles = Article.objects.exclude(status='draft')
# Combine filter and exclude
articles = Article.objects.filter(
is_published=True
).exclude(
category='archived'
)
Get a Single Object
Useget() to retrieve a single object. From Django’s source at query.py:636:
def get(self, *args, **kwargs):
"""
Perform the query and return a single object matching the given
keyword arguments.
"""
clone = self.filter(*args, **kwargs)
num = len(clone)
if num == 1:
return clone._result_cache[0]
if not num:
raise self.model.DoesNotExist(
"%s matching query does not exist." % self.model._meta.object_name
)
raise self.model.MultipleObjectsReturned(
"get() returned more than one %s" % self.model._meta.object_name
)
# Get article by ID
article = Article.objects.get(id=1)
# Get by unique field
article = Article.objects.get(slug='my-article')
# Multiple conditions
article = Article.objects.get(
title='Django Tutorial',
is_published=True
)
get() raises DoesNotExist if no object is found, and MultipleObjectsReturned if more than one object matches. Always handle these exceptions or use filter().first() for a safer alternative.Field Lookups
Django provides powerful field lookups using double underscores:# Exact match (default)
Article.objects.filter(title__exact='Django Tutorial')
Article.objects.filter(title='Django Tutorial') # Same as above
# Case-insensitive exact match
Article.objects.filter(title__iexact='django tutorial')
# Contains
Article.objects.filter(title__contains='Django')
Article.objects.filter(title__icontains='django') # Case-insensitive
# Starts with / ends with
Article.objects.filter(title__startswith='Django')
Article.objects.filter(title__endswith='Tutorial')
# Greater than / less than
Article.objects.filter(view_count__gt=100) # Greater than
Article.objects.filter(view_count__gte=100) # Greater than or equal
Article.objects.filter(view_count__lt=1000) # Less than
Article.objects.filter(view_count__lte=1000) # Less than or equal
# Range
Article.objects.filter(view_count__range=(100, 1000))
# In a list
Article.objects.filter(id__in=[1, 2, 3, 4])
Article.objects.filter(status__in=['published', 'featured'])
# NULL checks
Article.objects.filter(published_date__isnull=True)
Article.objects.filter(published_date__isnull=False)
Date Lookups
from datetime import date
# Year, month, day
Article.objects.filter(created_at__year=2024)
Article.objects.filter(created_at__month=12)
Article.objects.filter(created_at__day=25)
# Date
Article.objects.filter(created_at__date=date(2024, 12, 25))
# Week day (1=Sunday, 7=Saturday)
Article.objects.filter(created_at__week_day=1)
# Quarter
Article.objects.filter(created_at__quarter=4)
# Time
Article.objects.filter(created_at__hour=14)
Article.objects.filter(created_at__minute=30)
Complex Queries with Q Objects
UseQ objects for complex queries with OR, NOT, and nested conditions:
from django.db.models import Q
# OR condition
Article.objects.filter(
Q(is_published=True) | Q(is_featured=True)
)
# AND condition (explicitly)
Article.objects.filter(
Q(is_published=True) & Q(category='tech')
)
# NOT condition
Article.objects.filter(
~Q(status='draft')
)
# Complex nested conditions
Article.objects.filter(
Q(is_published=True) & (
Q(category='tech') | Q(category='science')
)
)
# Dynamic query building
query = Q()
if search_term:
query &= Q(title__icontains=search_term)
if category:
query &= Q(category=category)
if is_featured:
query &= Q(is_featured=True)
articles = Article.objects.filter(query)
Creating Objects
Method 1: Create and Save
# Create instance
article = Article(
title='My Article',
content='Article content here',
is_published=True
)
# Save to database
article.save()
Method 2: Using create()
From Django’s source atquery.py:674:
def create(self, **kwargs):
"""
Create a new object with the given kwargs, saving it to the database
and returning the created object.
"""
obj = self.model(**kwargs)
self._for_write = True
obj.save(force_insert=True, using=self.db)
return obj
# Create and save in one step
article = Article.objects.create(
title='My Article',
content='Article content here',
is_published=True
)
Method 3: get_or_create()
# Get existing or create new
article, created = Article.objects.get_or_create(
slug='my-article',
defaults={
'title': 'My Article',
'content': 'Article content',
'is_published': True
}
)
if created:
print("Article was created")
else:
print("Article already existed")
Method 4: update_or_create()
# Update existing or create new
article, created = Article.objects.update_or_create(
slug='my-article',
defaults={
'title': 'Updated Title',
'content': 'Updated content',
'is_published': True
}
)
Bulk Create
From Django’s source atquery.py:771:
articles = [
Article(title=f'Article {i}', content=f'Content {i}')
for i in range(100)
]
# Bulk insert (much faster than creating one by one)
Article.objects.bulk_create(articles, batch_size=1000)
# With ignore_conflicts (PostgreSQL, SQLite 3.24+)
Article.objects.bulk_create(
articles,
ignore_conflicts=True
)
# With update_conflicts (upsert)
Article.objects.bulk_create(
articles,
update_conflicts=True,
update_fields=['content', 'is_published'],
unique_fields=['slug']
)
bulk_create() is much more efficient than creating objects in a loop. Use it when inserting multiple objects.Updating Objects
Update Single Object
# Get object
article = Article.objects.get(id=1)
# Modify fields
article.title = 'Updated Title'
article.is_published = True
# Save to database
article.save()
# Save only specific fields (more efficient)
article.save(update_fields=['title', 'is_published'])
Bulk Update
# Update multiple objects
Article.objects.filter(category='tech').update(
is_featured=True,
view_count=0
)
# Update with F expressions (database-level operations)
from django.db.models import F
Article.objects.filter(is_published=True).update(
view_count=F('view_count') + 1
)
update() bypasses model save() methods and doesn’t send pre_save/post_save signals. Use it for bulk updates where you don’t need model logic to run.Bulk Update with bulk_update()
articles = Article.objects.filter(category='tech')
for article in articles:
article.view_count += 10
article.is_featured = True
# Update all at once
Article.objects.bulk_update(
articles,
fields=['view_count', 'is_featured'],
batch_size=1000
)
Deleting Objects
Delete Single Object
# Get and delete
article = Article.objects.get(id=1)
article.delete()
Bulk Delete
# Delete multiple objects
Article.objects.filter(is_published=False).delete()
# Delete all objects
Article.objects.all().delete()
Deletion is cascaded based on the
on_delete option in ForeignKey relationships. Be careful when deleting objects that are referenced by other models.QuerySet Methods
Chaining and Filtering
# QuerySets are lazy and chainable
qs = Article.objects.all() # No database hit yet
qs = qs.filter(is_published=True) # Still no database hit
qs = qs.exclude(category='archived') # Still no database hit
qs = qs.order_by('-created_at') # Still no database hit
# Database is hit when you evaluate the QuerySet
for article in qs: # NOW the database is queried
print(article.title)
Slicing
# Get first 5 articles
Article.objects.all()[:5]
# Get articles 5-10
Article.objects.all()[5:10]
# Get first article (more efficient than [0])
Article.objects.first()
# Get last article
Article.objects.last()
Negative indexing is not supported. Use
reverse() or order_by() instead.Ordering
# Ascending order
Article.objects.order_by('created_at')
# Descending order
Article.objects.order_by('-created_at')
# Multiple fields
Article.objects.order_by('-is_featured', '-created_at', 'title')
# Random order
Article.objects.order_by('?')
# Clear ordering
Article.objects.order_by().filter(is_published=True)
Distinct
# Remove duplicates
Article.objects.values('category').distinct()
# On PostgreSQL, you can specify fields
Article.objects.distinct('category', 'author')
Values and Values List
# Returns dictionaries
Article.objects.values('id', 'title', 'created_at')
# [{'id': 1, 'title': 'Article 1', 'created_at': ...}, ...]
# Returns tuples
Article.objects.values_list('id', 'title')
# [(1, 'Article 1'), (2, 'Article 2'), ...]
# Flat list (single field)
Article.objects.values_list('title', flat=True)
# ['Article 1', 'Article 2', ...]
# Named tuples
Article.objects.values_list('id', 'title', named=True)
Counting and Existence
# Count objects
count = Article.objects.filter(is_published=True).count()
# Check existence (more efficient than count)
if Article.objects.filter(slug='my-article').exists():
print("Article exists")
# Get one or None
article = Article.objects.filter(slug='my-article').first()
Related Object Queries
# Forward relationship (ForeignKey)
book = Book.objects.get(id=1)
author = book.author # Follows the ForeignKey
# Reverse relationship
author = Author.objects.get(id=1)
books = author.books.all() # Uses related_name
# Filter by related field
Book.objects.filter(author__name='John Doe')
# Span multiple relationships
Book.objects.filter(author__country__name='USA')
Select Related and Prefetch Related
Optimize queries that access related objects:# select_related (for ForeignKey and OneToOne)
# Uses SQL JOIN to fetch related objects in single query
books = Book.objects.select_related('author').all()
for book in books:
print(book.author.name) # No additional query
# Multiple relations
books = Book.objects.select_related('author', 'publisher').all()
# Nested relations
books = Book.objects.select_related('author__country').all()
# prefetch_related (for ManyToMany and reverse ForeignKey)
# Uses separate queries and does joining in Python
authors = Author.objects.prefetch_related('books').all()
for author in authors:
for book in author.books.all(): # No additional query
print(book.title)
# Combine both
books = Book.objects.select_related('author').prefetch_related('tags').all()
Use
select_related() for single-valued relationships (ForeignKey, OneToOneField) and prefetch_related() for multi-valued relationships (ManyToManyField, reverse ForeignKey).Raw SQL
When you need to execute raw SQL:# Raw query
articles = Article.objects.raw(
'SELECT * FROM articles WHERE is_published = %s',
[True]
)
# Execute custom SQL
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("UPDATE articles SET view_count = view_count + 1 WHERE id = %s", [1])
cursor.execute("SELECT * FROM articles WHERE view_count > %s", [100])
rows = cursor.fetchall()
Always use parameter substitution (the
%s placeholder) to prevent SQL injection attacks. Never use string formatting or concatenation with user input.