Python Django ORM: filter, get, exclude, create, update, and delete
Learn how to use Django's ORM methods filter, get, exclude, create, update, and delete to query and modify database records.
Django's ORM provides a focused set of methods for common database operations: filter(), get(), exclude(), create(), update(), and delete(). This article explains each operation, its syntax, return types, and practical considerations, with examples you can adapt to your own models.
Querying with filter and get
The filter() method returns a QuerySet containing objects that match the given lookup parameters. It is lazy, meaning the database query is not executed until the QuerySet is evaluated, such as when iterating over it or calling list() on it.
from myapp.models import Product # Returns all products with price > 100 expensive_products = Product.objects.filter(price__gt=100)
get() returns a single object that matches the lookup. It raises Product.DoesNotExist if no object matches, and Product.MultipleObjectsReturned if more than one object matches. Use get() when you expect exactly one result and want to handle the absence explicitly.
try: product = Product.objects.get(id=42) except Product.DoesNotExist: product = None
get() is not lazy; it executes the query immediately. If you only need to check existence, filter().exists() is more efficient.
Excluding Records with exclude
The exclude() method returns a QuerySet containing objects that do not match the given lookup parameters. It is the logical opposite of filter() and can be chained with other queryset methods.
# Returns all products that are not in the 'discontinued' category active_products = Product.objects.exclude(category='discontinued')
exclude() is also lazy. You can combine filter() and exclude() to build complex queries:
# Products with price > 100 and not in the 'clearance' category result = Product.objects.filter(price__gt=100).exclude(category='clearance')
Each call returns a new QuerySet, so you can chain as many as needed without affecting the original.
Creating Records with create and save
The create() method is a convenience that creates and saves an object in one step. It returns the saved instance.
product = Product.objects.create(name='Widget', price=19.99)
In its simplest form, this is equivalent to:
product = Product(name='Widget', price=19.99) product.save(force_insert=True)
Use save() when you need to perform additional logic before saving, such as setting a field based on another value or validating custom constraints. create() calls save(force_insert=True) under the hood, so any custom save() logic on the model still runs.
When creating many objects at once, bulk_create() can be significantly faster than calling create() in a loop, because it uses a single SQL INSERT statement. Note that bulk_create() does not call save() and does not send model signals.
Updating Records with update and save
To update a single instance, modify its attributes and call save():
product = Product.objects.get(id=1) product.price = 24.99 product.save()
The update() method on a QuerySet performs a bulk update without loading each object into Python. It returns the number of rows affected.
updated = Product.objects.filter(category='clearance').update(price=0)
update() does not call save() on individual models, so signals and custom save() logic are not triggered. Use it for simple field updates that do not require model-level processing.
For bulk updates that need to call save() or handle per-row logic, iterate over the queryset and call save() individually, but be aware of the performance cost.
Deleting Records with delete
Call delete() on a model instance to remove that row:
product = Product.objects.get(id=1) product.delete()
Call delete() on a QuerySet to delete all matching rows in one database operation:
Product.objects.filter(category='discontinued').delete()
The delete() method returns a dictionary mapping object types to the number of deleted objects, including cascaded deletions. By default, deleting a model instance also deletes related objects that use on_delete=models.CASCADE. Use on_delete=models.PROTECT, models.SET_NULL, or another option to control what happens to related rows.
Chaining Queries and Using Q Objects
filter() and exclude() accept keyword arguments that are combined with AND. To express OR conditions, use Q objects from django.db.models.
from django.db.models import Q # Products with price > 100 OR category = 'premium' result = Product.objects.filter(Q(price__gt=100) | Q(category='premium'))
You can also negate a Q object with ~ to achieve complex exclusions:
# Products that are not (price > 100 AND category = 'premium') result = Product.objects.exclude(Q(price__gt=100) & Q(category='premium'))
Chaining filter() and exclude() with Q objects keeps the query readable and avoids multiple round trips to the database.
Performance and Operational Considerations
When working with related models, use select_related() for foreign keys and prefetch_related() for many-to-many and reverse relations to avoid the N+1 query problem.
# Without select_related, each order triggers a query for its customer orders = Order.objects.select_related('customer').all()
For bulk operations, prefer bulk_create() and bulk_update() over individual save() calls. bulk_update() updates multiple rows without calling save() on each instance:
products = list(Product.objects.filter(category='clearance')) for product in products: product.price = 0 Product.objects.bulk_update(products, ['price'])
Transactions are important when performing multiple related operations. Use transaction.atomic() to ensure all changes are committed or rolled back together.
from django.db import transaction with transaction.atomic(): order = Order.objects.create(customer=customer) for item in items: OrderItem.objects.create(order=order, **item)
Finally, remember that filter() and exclude() are lazy, so you can build a query step by step without hitting the database until you evaluate it. This allows you to conditionally add filters based on user input without duplicating code. Understanding these methods and their performance implications will help you write scalable Django applications.