CRUD Operations
As explained earlier, a model class represents a database table, and an instance of that class represents a particular record in that table. This page explains how to perform "CRUD" operations (Create, Read, Update, Delete) on these objects in Django.
Throughout this page, we will use three related models: Author, Blog, and Entry. In this setup, Entry has a foreign key reference to Blog and a many-to-many relationship to Author.
Create Objects
To add a new record to your database, you create an instance of your model class and call the save() method. This performs an INSERT SQL statement behind the scenes.
# Create the instance
b = Blog(name="Beatles Blog", tagline="All the latest news.")
# Write to the database
b.save()You can also do this in one step using create() method:
# Creates and saves immediately
b = Blog.objects.create(name="Beatles Blog", tagline="All the latest news.")Bulk Create
You can also insert multiple objects in a single query using bulk_create().
# Create a list of unsaved Entry objects
entries = [
Entry(headline="Entry 1", blog=b),
Entry(headline="Entry 2", blog=b),
Entry(headline="Entry 3", blog=b),
]
# Save them all in one SQL query
Entry.objects.bulk_create(entries)Retrieve Objects
To retrieve objects from your database, you'll need to get a QuerySet by using your model's Manager (usually called objects).
A QuerySet represents a collection of objects from your database. In SQL terms, a QuerySet equates to a SELECT statement.
Here are common ways to retrieve objects using the QuerySet:
Blog.objects.all()gets every record in the table.Blog.objects.filter(year=2006)gets records that match the criteria. This is like applying a WHERE clause on the SELECT statement in SQL.Blog.objects.exclude(year=2006)gets records that do not match. This is equivalent to NOT IN filter in SQL.Blog.objects.get(pk=1)gets a single record. This raises an exception if the record does not exist (DoesNotExist) or if more than one exists (MultipleObjectsReturned).
Chaining Filters & Laziness
QuerySets are lazy. You can chain multiple filters together (Ex: filter(...).exclude(...)), but Django will not actually hit the database until you try to use the data (like printing it or looping through it).
For example, the Blog.objects.filter(year=2006) above is essentially the same as Blog.objects.all().filter(year=2006).
Consider the example below. It might look like we are hitting the database three times, but in reality, Django is only building a single, optimized SQL query in memory.
# 1. Create a base QuerySet (No DB access yet)
q = Entry.objects.filter(headline__startswith="What")
# 2. Refine it with another filter (Still no DB access)
q = q.filter(pub_date__lte=datetime.date.today())
# 3. Refine it further with an exclusion (Still no DB access)
q = q.exclude(body_text__icontains="food")
# 4. Evaluate the data
# BAM! Only NOW does Django hit the database with the final SQL query.
print(q)Notice that the database is hit only when the print is invoked.
Field Lookups (The Double Underscore)
Django uses a double underscore syntax (__) to define specific conditions for your filters. If you don't provide one, it defaults to an exact match.
# exact match
Entry.objects.filter(pub_date="2006-01-01")
# 'lte' means Less Than or Equal to
Entry.objects.filter(pub_date__lte="2006-01-01")
# 'contains' is like a search
Entry.objects.filter(headline__contains="Lennon")Relationships
Django offers a powerful way to “follow” relationships in lookups using the double underscore syntax (__). It handles all the SQL JOINs for you automatically behind the scenes.
Forward Relationships
You can filter a model based on fields in a related model it points to.
The following retrieves all Entry objects linked to a Blog named 'Beatles Blog':
# Entry -> linked to -> Blog -> check name
Entry.objects.filter(blog__name="Beatles Blog")Reverse Relationships
You can also filter a model based on the models that point to it. To do this, use the lowercase name of the related model (unless you set a specific related_query_name).
The following retrieves all Blog objects that have at least one Entry with a headline containing 'Lennon':
# Blog -> looked up by -> entry -> check headline
Blog.objects.filter(entry__headline__contains="Lennon")Note: If you had defined related_query_name='post' on the ForeignKey, you would use post__headline instead.
For a complete list of all available QuerySet methods and field lookups, refer to the QuerySet API Reference.
Optimization: select_related and prefetch_related
When you access related objects (like a Blog from an Entry), Django normally hits the database every time you access a related object. This can lead to the N+1 query problem, where one initial query is followed by many smaller queries inside a loop. Django provides select_related and prefetch_related to solve this efficiently.
select_related (SQL JOIN)
Use this for single-valued relationships: ForeignKey and OneToOneField. It works by creating an SQL JOIN and including the related fields in the same SELECT statement.
# Hits the database ONCE
# Retrieves the Entry AND its related Blog in one query
entry = Entry.objects.select_related("blog").get(id=1)
print(entry.blog.name) # Already in memory; no database hitYou can also follow relationships across multiple levels:
Entry.objects.select_related('blog__owner')prefetch_related (Multi-query)
Use this for multi-valued relationships: ManyToManyField and reverse ForeignKeys. Because joining these in SQL can create massive, redundant result sets, Django performs a separate query for each table and combines the results together in Python.
# Hits the database TWICE
# 1. Gets the Blogs
# 2. Gets all related Entries in one batch
blog = Blog.objects.prefetch_related("entry_set").get(id=1)
for entry in blog.entry_set.all():
print(entry.headline) # Already in memory; no database hitUsing Both Together
You can combine select_related and prefetch_related in the same QuerySet when dealing with mixed relationship types.
Entry.objects.select_related('blog').prefetch_related('authors')In this case, Django joins the Blog table directly while fetching authors in a separate query, producing an optimal number of database hits.
Querying JSONField
If your model uses a JSONField, you can use the double underscore syntax to filter based on keys within the JSON data. This allows you to traverse the data structure as if it were a related model.
To access a value inside a JSON object, join the field name and the key name with a double underscore.
# Finds records where the 'details' JSON has a 'type' key equal to 'admin'
User.objects.filter(details__type='admin')Nested Paths
If the JSON data is deeply nested, you can chain multiple keys together to reach the desired value.
# Navigates through details -> profile -> city
User.objects.filter(details__profile__city='New York')Containment and Key Existence
You can also perform lookups based on whether a JSON object contains specific data or whether a key exists at all.
The contains lookup checks if the provided dictionary is a subset of the JSON field.
# Returns records where 'details' contains this specific key-value pair
User.objects.filter(details__contains={'status': 'active'})The has_key lookup checks for the presence of a specific key.
# Returns records where the 'details' field has a key named 'metadata'
User.objects.filter(details__has_key='metadata')Type-Specific Lookups
When you retrieve a value from a JSON field, Django treats it as the data type it finds (string, integer, boolean, etc.). You can use standard lookups like gte or icontains on these values just like normal fields.
# Accesses a nested integer and checks if it is greater than 10
User.objects.filter(details__score__gt=10)Key Transformation (KT)
The KT expression represents a specific key or path within a JSON field. You use it when you need to reference a JSON key dynamically or when you want to use the JSON data in an annotation.
Dynamic Key Access
If the key name is stored in a variable, the standard double underscore syntax will not work. KT allows you to build that path dynamically.
from django.db.models.fields.json import KT
# If key_name is a variable that could be 'status', 'type', etc.
key_name = 'status'
User.objects.filter(KT(f'details__{key_name}')='active')Using KT in Annotations
KT is helpful for "extracting" a value from a JSON field and treating it like a standalone field for ordering or further filtering.
# Extract 'age' from the JSON and sort the results by it
User.objects.annotate(
extracted_age=KT('details__profile__age')
).order_by('extracted_age')Complex Lookups (Q & F Objects)
Standard keyword arguments (like filter(name="John")) are limited to "AND" logic and comparing fields to constant values. For more complex queries, Django provides Q and F objects.
Q() Objects (OR Queries)
Q objects allow you to build complex queries using logical OR (|) and NOT (~) operators.
from django.db.models import Q
# Find entries where headline starts with "Who" OR "What"
Entry.objects.filter(Q(headline__startswith='Who') | Q(headline__startswith='What'))
# Find entries where headline is NOT "Who"
Entry.objects.filter(~Q(headline="Who"))F() Objects (Field-to-Field Comparison)
F objects allow you to reference a model field's value within a query. This is useful for comparing two fields on the same model instance or performing atomic updates.
from django.db.models import F
# Find entries where the number of comments is greater than pingbacks
Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))
# Atomic Update: Increment rating by 1 without fetching the object first
Entry.objects.filter(pk=1).update(rating=F('rating') + 1)Update Objects
To update data that already exists in the database, you retrieve the object, modify its attributes in Python, and then call the save() method. This executes an SQL UPDATE statement behind the scenes.
# 1. Retrieve the object
b = Blog.objects.get(pk=1)
# 2. Modify the attribute
b.name = "New Name"
# 3. Save the changes to the database
b.save()Update Multiple Objects
To update multiple objects in a single query, use the update() method on a QuerySet. This is useful when you want to set one or more fields to the same value across many records.
# Set the 'headline' field for all entries published in 2007
Entry.objects.filter(pub_date__year=2007).update(headline="Everything is the same")Key Behaviors & Limitations
- Scope: You can only update non-relation fields and
ForeignKeyfields (by passing the model instance). You cannot update fields on related models (joins) in a singleupdate()call. - Return Value: The
update()method returns an integer representing the number of matched rows (which may be higher than the number of rows actually modified if some already held the new value). - No Signals or
save(): Because this is a direct SQL operation, the model'ssave()method is never called. Consequently,pre_saveandpost_savesignals are not emitted, and options likeauto_now(for date fields) are not automatically updated. (Signals are covered later)
Using F() Expressions
You can use F expressions to update a field based on its current value (Ex: incrementing a counter).
from django.db.models import F
# Increment the 'number_of_pingbacks' by 1 for every entry
Entry.objects.update(number_of_pingbacks=F("number_of_pingbacks") + 1)Note: Unlike in filters, you CANNOT use joins with F() expressions inside an update() call, you can only reference fields local to the model.
Update Foreign Keys
To update a foreign key, assign the new related object instance to the field.
entry = Entry.objects.get(pk=1)
cheese_blog = Blog.objects.get(name="Cheddar Talk")
# Assign the new blog object
entry.blog = cheese_blog
entry.save()Update Many-to-Many Fields
You cannot assign a list to a many-to-many field directly. Instead, use the helper methods provided by the relationship manager.
joe = Author.objects.create(name="Joe")
entry = Entry.objects.get(pk=1)
# .add() appends to the existing list
entry.authors.add(joe)
# .set() replaces the entire list
entry.authors.set([joe, other_author])Bulk Update
You can also update specific fields on multiple existing objects using bulk_update().
entries = Entry.objects.all()
for entry in entries:
entry.headline = "Updated Headline"
# Only updates the 'headline' field for all entries in one query
Entry.objects.bulk_update(entries, ['headline'])Delete Objects
To remove a record from the database, use the delete() method. This creates an SQL DELETE statement behind the scenes.
b = Blog.objects.get(pk=1)
b.delete()Bulk Delete
You can also call delete() on a QuerySet to remove all objects that match a filter. This is more efficient than looping through objects and deleting them one by one.
# Delete all entries published in 2005
Entry.objects.filter(pub_date__year=2005).delete()Safety Mechanism
Django prevents you from accidentally deleting an entire table. The delete() method does not exist on the Model Manager itself. You must strictly select the objects you want to delete first, even if you want to delete everything.
# This fails (Safety check)
Entry.objects.delete()
# This works (Explicitly selecting all)
Entry.objects.all().delete()Common Exceptions
When performing CRUD operations, you may encounter various exceptions. It is good practice to handle these using try/except blocks to prevent your application from crashing.
Standard Exceptions
| Exception | Description |
|---|---|
ValidationError | Raised when data fails validation logic, such as calling full_clean() on a model instance or during form processing. |
IntegrityError | Raised when a database constraint is violated, such as a unique constraint or a foreign key requirement. (Imported from django.db) |
DatabaseError | The base class for all database-related errors. Most specific database exceptions inherit from this. |
ObjectDoesNotExist | The base class for all DoesNotExist exceptions. Useful for catching "not found" errors for any model. |
FieldError | Raised when a query references a field name that does not exist on the model. |
ProtectedError | Raised during a delete() operation if a foreign key with on_delete=models.PROTECT prevents the deletion. |
RestrictedError | Similar to ProtectedError, but raised when on_delete=models.RESTRICT prevents deletion. |
Model-Specific Exceptions
Every model class automatically generates two specific exception classes. These are distinct for each model.
| Exception | Description |
|---|---|
MyModel.DoesNotExist | Raised when MyModel.objects.get() fails to find a matching record. It inherits from ObjectDoesNotExist. |
MyModel.MultipleObjectsReturned | Raised when MyModel.objects.get() finds more than one record but expected exactly one. |
Transactions
TIP
You can disable Django's autocommit behavior for a given database using AUTOCOMMIT setting.
By default, Django runs in autocommit mode, meaning every single database operation is immediately committed. To bundle multiple operations into a single atomic unit (where all succeed or all fail), use transaction.atomic().
from django.db import transaction
try:
with transaction.atomic():
# Both of these must succeed, or NEITHER will happen
b = Blog.objects.create(name="New Blog")
Entry.objects.create(blog=b, headline="First Post")
# If an error occurs here, the blog and entry are rolled back
raise Exception("Oops!")
except Exception:
print("Transaction failed. Nothing was saved.")Exception Handling
You should avoid handling exceptions inside atomic() block, because Django looks at whether the atomic block exited normally or with an exception to determine whether to commit or roll back. If you catch and handle the exceptions inside the block, you may hide from Django the fact that a problem has happened. This can result in unexpected behavior.
The correct way to handle exceptions is to wrap the entire atomic block in it. Example:
from django.db import DatabaseError, transaction
try:
with transaction.atomic():
some_transaction()
except DatabaseError:
handle_exception()Compare Objects
To compare two model instances in Django, use the standard Python comparison operator (==) operator. Django considers two objects equal if they have the same primary key value and belong to the same model class.
some_entry == other_entry
# is the same as
some_entry.id == other_entry.idEven if your model uses a different primary key field (not id), Django still compares the correct primary key field. For example, if the primary key is named slug, then:
some_obj == other_obj
# is the same as
some_obj.slug == other_obj.slugCopying Objects
Django doesn’t provide a built-in method to clone or duplicate a model instance, but you can manually create a copy by reusing its field values and saving it as a new object.
To copy a model instance in Django, you can set its primary key (pk) to None and then call save(). This tells Django to treat the instance as a new record and perform an SQL INSERT instead of an UPDATE.
blog = Blog.objects.get(pk=1)
blog.pk = None
blog.save() # A new object is createdInheritance Considerations
If you are using multi-table inheritance (where one model subclasses another), you must set both the pk and the id field (the pointer to the parent model) to None.
entry = Entry.objects.get(pk=1)
entry.pk = None
entry.id = None
entry.save()Many-to-Many Relationships
Many-to-many relationships are not copied automatically when you reset the primary key. Because these relations rely on the instance already existing in the database, you must save the new copy first and then manually copy the related data.
old_authors = entry.authors.all()
# Create the copy
entry.pk = None
entry.id = None
entry.save()
# Manually associate the authors to the new copy
entry.authors.set(old_authors)Using Raw SQL in Django
Django provides options to write SQL directly when a query is too complex for Django’s ORM to handle. This can be useful for specialized queries or performance-critical operations.
TIP
Never insert user input directly into raw SQL using string formatting. Always pass parameters as a separate list or dictionary to execute() (or raw()), so that Django can handle data escaping and protect your application from SQL injection.
The Raw Method
You can use the raw function on a model manager (Manager.raw()) to execute SELECT statements directly and return model instances. The SQL must return all the fields (including the model's primary key) required to build the model objects. The method signature is:
# raw_query = a raw SELECT SQL string
# params = optional list or tuple of parameters for parameterized queries
# translations = optional dict to map SQL column names to model field names
Model.objects.raw(raw_query, params=(), translations=None)Example:
# Using raw() to get model instances
for p in Person.objects.raw('SELECT * FROM myapp_person'):
print(p.first_name)
# Mapping specific columns to model fields
name_map = {'first': 'first_name', 'last': 'last_name'}
people = Person.objects.raw('SELECT id, first, last FROM myapp_person', translations=name_map)Using Database Cursors
When you need to perform actions that do not fit into a model or when executing complex queries, you can execute SQL directly using a database cursor via django.db.connection.
from django.db import connection
def my_custom_sql():
with connection.cursor() as cursor:
cursor.execute("UPDATE my_table SET last_name=%s WHERE id=%s", ['Lennon', 1])
cursor.execute("SELECT id, last_name FROM my_table WHERE id=%s", [1])
row = cursor.fetchone()
return row