Query MongoDB Documents and Collections
Querying data is done through the Object-Document Mapping (ODM), which allows you to interact with MongoDB using Python code instead of pymongo query documents. At the core of querying are QuerySets, which represent a lazy collection of database objects that can be filtered, ordered, and manipulated. A QuerySet does not hit the database immediately; instead, it is evaluated only when the data is actually needed. This lazy behavior allows QuerySets to be efficiently reused and combined. QuerySets form the foundation for all read operations.
The most common methods to retrieve data are all(), get(), and filter().
Both all() and filter() return a new QuerySet. The filter() method, which returns a new QuerySet
containing objects that match given conditions. Filters are non-destructive, meaning they always return
a new QuerySet rather than modifying the original one.
Djongo supports chaining filters, allowing multiple conditions to
be applied sequentially in a clear and readable way. Each chained filter further narrows the result set.
Use the double-underscore (__) to define more advanced queries and field lookups.
This syntax separates a field name from a lookup type, such as exact, icontains, gt, or lte.
These field lookups determine how comparisons are made, whether for exact matches, partial text searches,
or numerical ranges. If no lookup is specified, the query defaults to an exact match.
The double-underscore syntax also enables complex queries without writing complex query documents.
QuerySets can be limited using slicing, such as [:10], which translates to aggregation $limit operators.
This allows developers to efficiently control how many records are retrieved from the database.
Ordering results before limiting them ensures predictable output. Limiting QuerySets is especially useful
for performance optimization. Because QuerySets are lazy, slicing does not immediately execute the query until needed.
You can create lookups that span relationships, making it easy to query across a foreign key and many-to-many relationships. By chaining related field names with double underscores, you can filter based on attributes of related models. For example, querying posts by an author’s username requires no joins written by hand. Djongo automatically generates the necessary joins behind the scenes. This feature greatly simplifies working with relational data.
Accessing related objects is another feature where related objects can be accessed
using attributes, such as reverse relations. Methods like select_related() and prefetch_related()
help optimize database access when working with related data. These tools reduce the number of queries
needed and improve application performance.
Query documents
Define the models in models.py to setup the relevant collections in the database.
models.py
from djongo import models
class Blog(models.Model):
name = models.CharField(max_length=100)
content = models.TextField()
class Meta:
abstract = True
class Author(models.Model):
name = models.CharField(max_length=100)
verified = models.BooleanField()
class Meta:
abstract = True
class Entry(models.Model):
_id = models.ObjectIdField()
headline = models.CharField(max_length=255)
blog = models.EmbeddedField(
model_container=Blog
)
authors = models.ArrayField(model_container=Author)
Djongo contains several methods for quering the database.
Select all documents in a Collection
To retrieve all documents in a collection use the all() method. It returns a QuerySet which does not
hit the database until iterated over.
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.all()
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(projection=['_id', 'headline', 'blog', 'authors'])
More information can be found in the Django reference documentation
Select a unique document from a Collection
To retrieve a single unique document from the collection use get(). The method takes kwargs that can be used to specify
equality conditions. The lhs of the kwarg specifies the document field name, while the rhs part specifies the value the field name must match. The method directly hits the database when called and does not need to be iterated over.
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.get(headline="The Headline")
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'headline': {'$eq': 'The Headline'}},
limit=21,
projection=['_id', 'headline', 'blog', 'authors'])
If there are no results that match the query, get() will raise a DoesNotExist exception.
If more than one item matches the query, get() will raise a MultipleObjectsReturned exception.
More information can be found in the Django reference documentation.
Select a subset of documents from a Collection
A subset of documents is selected by using filter() and specifying the query conditions.
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(headline="The Headline")
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'headline': {'$eq': 'The Headline'}},
projection=['_id', 'headline', 'blog', 'authors'])
Specify equality condition
To specify equality conditions, pass them as keyword arguments to either the filter() or get() methods.
Kwargs are specified with the expression: field=value. Multiple kwargs can be provided at the same time.
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(headline="The Headline", blog={'name': 'James'})
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'$and': [{'blog.name': {'$eq': 'James'}}, {'headline': {'$eq': 'The Headline'}}]},
projection=['_id', 'headline', 'blog', 'authors'])
Specify AND conditions
By default, all kwargs provided to filter() and get() are considered as AND conditions.
No explict syntax is needed for pure AND queries.
main.py
from django.core.management.base import BaseCommand
from models import Entry
from bson import ObjectId
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.get(headline="The Headline",
_id=ObjectId('61d2d6c07d26e98969f8b8c2'))
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'$and': [{'_id': {'$eq': ObjectId('61d2d6c07d26e98969f8b8c2')}},
{'headline': {'$eq': 'The Headline'}}]},
limit=21,
projection=['_id', 'headline', 'blog', 'authors'])
Specify OR conditions
OR conditions are specified in Djongo by using Q objects
main.py
from django.core.management.base import BaseCommand
from models import Entry
from django.db.models import Q
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(
Q(headline="The Headline") | Q(blog={'name': 'James'})
)
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'$or': [{'headline': {'$eq': 'The Headline'}},
{'blog.name': {'$eq': 'James'}}]},
projection=['_id', 'headline', 'blog', 'authors'])
Specify AND as well as OR conditions
A combination of AND and OR conditions is specified by using Q objects again.
main.py
from django.core.management.base import BaseCommand
from models import Entry
from django.db.models import Q
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(
(Q(headline="The Headline") | Q(blog={'name': 'James'})) & Q(authors={'0.name': 'James'})
)
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'$and': [
{'$or': [
{'headline': {'$eq': 'The Headline'}},
{'blog.name': {'$eq': 'James'}}
]},
{'authors.0.name': {'$eq': 'James'}}
]},
projection=['_id', 'headline', 'blog', 'authors'])
Specify conditions using query predicates
Djongo supports several MongoDB query predicates through field lookups. Use the double-underscore (__) to specify the field lookup
after the field name, <field_name>__<field_lookup>.
The following lookups are supported exact, iexact, contains, icontains,
gte, lte, in, and range.
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(headline__in=['Breaking News', 'The Headline'])
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'headline': {'$in': ['Breaking News', 'The Headline']}},
projection=['_id', 'headline', 'blog', 'authors'])
Evaluating QuerySets
A QuerySet executes when it’s evaluated — when you iterate over it, convert it to a list,
access a slice, or call count(), exists(), first(), or last().
The results are cached after the first evaluation,
so repeated iterations don’t hit the database again unless you call all() to refresh.
This lazy and cached behavior helps minimize database traffic and improve performance.
You can learn more about making queries here.
qs = Entry.objects.filter(published_date__year=2024)
print(list(qs)) # Executes the query
print(list(qs)) # Uses the cached results, no new query
Specifying Query Options
Djongo lets you specify the configuration of the find command into your QuerySets. Call the configure method on a QuerySet to configure the find query. All options supported by aggregate or find can be included as kwargs. Example of valid arguments:
Arguments
| Argument | Type | Description |
|---|---|---|
allowDiskUse | boolean | Enables writing to temporary files |
collation | Collation | Used to specify the collation. Takes an instance of Collation |
Example
Blog.objects.filter(name='John Lennon').configure(hint=['-tagline'])
This generates the following pymongo find query:
db.blog.find({'name': 'John Lennon'}, hint=[('tagline', pymongo.DESCENDING)])
Embedded documents
A Nested field is one that lies inside an embedded document. Querying nested fields is done by specifying the field names as keys of a dict. Next, the value you want the nested field to match, is specified as the value of the dict.
Alternatively, the nested field you want to query can also be specified using the dot notation.
Query nested fields
To query all Blog with the nested field name equal to Beatles, run the following query:
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(blog={'name': 'Beatles'})
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'blog.name': {'$eq': 'Beatles'}},
projection=['_id', 'headline', 'blog', 'authors'])
Query nested fields (with Dot Notation)
Deeply nested fields can be queried using the dot notation.
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(blog={'tagline.subtitle': 'Beatles reunion tour'})
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'blog.tagline.subtitle': {'$eq': 'Beatles reunion tour'}},
projection=['_id', 'headline', 'blog', 'authors'])
Arrays
Djongo uses a mixture of Django query syntax and MongoDB query syntax.
Consider a query to retrieve all entries made by the author Paul. Using ManyToManyField this requires 2 SQL queries.
First selects the id for author Paul from the author table.
Next, a JOIN with entry_authors and entry gives the corresponding entries.
With ArrayField the query reduces to a single query with no joins:
Match an Array
To specify equality condition on an array, specify the field name as the lhs of the kwarg. The array value to match against, including the order of the elements, forms the rhs of the kwarg.
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(authors=[
{ "name": "James", "verified": True },
{ "name": "Guest", "verified": False }
]
)
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'authors': {'$eq': [
{'name': 'James', 'verified': True}, {'name': 'Guest', 'verified': False}
]}},
projection=['_id', 'headline', 'blog', 'authors'])
Query an Array for an element
Djongo lets you get even more specific with your queries. To query all entries where the second author is Guest:
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(authors={'1.name': 'Guest'})
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'authors.1.name': {'$eq': 'Guest'}},
projection=['_id', 'headline', 'blog', 'authors'])
In MongoDB the first element in the array starts at index 0. Djongo follows the same system.
Arrays of Embedded documents
Specify a query condition on a field in an Array of documents
Djongo lets you specify query operators on elements of an Array
main.py
from django.core.management.base import BaseCommand
from models import Entry
class Command(BaseCommand):
help = "Main execution file"
def handle(self, *args, **options):
entry = Entry.objects.filter(authors__lt={'name': 'James'})
print(list(entry))
The following pymongo commands are generated:
db["myapp_entry"].find(filter={'authors.name': {'$lt': 'James'}},
projection=['_id', 'headline', 'blog', 'authors'])
Query an Array
Djongo uses a mixture of Django query syntax and MongoDB query syntax.
Consider a query to retrieve all entries made by the author Paul. Using ManyToManyField this requires 2 SQL queries.
First selects the id for author Paul from the author table.
Next, a JOIN with entry_authors and entry gives the corresponding entries.
With ArrayField the query reduces to a single simple query:
entries = Entry.objects.filter(authors={'name': 'Paul'})
Djongo lets you get even more specific with your queries. To query all entries where the third author is Paul:
entries = Entry.objects.filter(authors={'2.name': 'Paul'})
Note: In MongoDB the first element in the array starts at index 0.
Aggregation
Chaining Filters and Field Lookups
You can chain filters to build complex queries. Each filter call returns a new QuerySet, allowing you to progressively narrow down results.
QuerySets remain lazy until evaluated, so chaining does not hit the database immediately. This pattern allows for clean, readable, composable queries.
Ordering, Slicing, and Limiting
You can order results using order_by() and limit them using Python slicing syntax.
Ordering accepts one or more field names, with a - prefix for descending order.
Slicing translates to SQL/MongoDB LIMIT and OFFSET.
You can also use first() and last() to get a single record or None if none exist.
These tools let you fetch only the portion of data you need efficiently.
latest_five = Entry.objects.order_by('-published_date')[:5]
first_post = Entry.objects.order_by('published_date').first()
Aggregations and Related Lookups
Django ORM (and therefore Djongo) supports aggregations with functions like Count,
Sum, Avg, Min, and Max. Use aggregate() to return a single summary value,
or annotate() to add computed values to each result row.
You can traverse related
fields using double underscores (following ForeignKey, OneToOneField, or ManyToManyField).
This lets you perform joins and calculations without writing raw database queries.
from djongo.models import Count
authors = Author.objects.annotate(num_posts=Count('post'))
for a in authors:
print(a.name, a.num_posts)
