Get Started

Install

  • Install the latest version of djongo from:
  pip install --extra-index-url https://pypi.djongomapper.com/latest-updates/ djongo
copy code
  • Into settings.py file of your project, add:
  DATABASES = {
      'default': {
          'ENGINE': 'djongo',
          'NAME': 'your-db-name',
      }
  }
copy code

Prerequisites

  • You have an account.
  • Your access tokens have been successfully setup.
  • Alternatively, you can install an older version directly from pypi:
  pip install djongo
copy code
  • Or, from github:
pip install git+https://github.com/djongo/djongo.git
copy code

Models

A Model is the single, canonical source of information about the data your application stores. It is defined as a Python class that subclasses djongo.models.Model. Each attribute of the class represents a MongoDB document field.

Each model maps to one MongoDB collection and encapsulates the structure and behavior of that collection in one place. Because models are plain Python classes, you can add helper methods and properties to encapsulate domain logic close to the data they operate on.

Defining models in code makes your schema explicit, reviewable, and easy to test. More details on models can be found here

from djongo import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    published_on = models.DateTimeField(null=True, blank=True)
copy code

Fields

Fields are the building blocks of models. Djongo supports all of Django field types, for example CharField, TextField, IntegerField, BooleanField, and DateTimeField. Each field accepts options such as max_length, blank, null, default, and choices to control validation and storage behavior.

Models also express relationships at the schema level using ForeignKey, OneToOneField, and ManyToManyField. The inner Meta class lets you provide model-wide configuration such as verbose_name, custom database table names, and ordering preferences.

Djongo adds extra fields for supporting MongoDB features like embedded documents.

from djongo import models

class Author(models.Model):
    name = models.CharField(max_length=100)

class Post(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    title = models.CharField(max_length=255)
    tags = models.ManyToManyField('Tag', blank=True)

    class Meta:
        verbose_name = "blog post"
        ordering = ['-id']
copy code

EmbeddedField

The EmbeddedField lets you embed a document inside a document. Use it for creating MongoDB embedded documents. Nest a dict inside a model with the EmbeddedField. The model_container is used to describe the structure of the data stored.

from djongo import models

class Blog(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        abstract = True

class Entry(models.Model):
    blog = models.EmbeddedField(
        model_container=Blog
    )    
    headline = models.CharField(max_length=255)    

e = Entry()
e.blog = {
    'name': 'Djongo'
}
e.headline = 'The Django MongoDB connector'
e.save()
copy code

Learn more about embedded fields here.

ArrayField

The ArrayField lets you store a list of embedded documents inside a document. Nest a list of dict inside a model for doing this.

from djongo import models

class Entry(models.Model):
    blog = models.ArrayField(
        model_container=Blog
    )    
    headline = models.CharField(max_length=255)    

e = Entry()
e.blog = [
    {'name': 'Djongo'}, {'name': 'Django'}, {'name': 'MongoDB'}
]
e.headline = 'Djongo is the best Django and MongoDB connector'
e.save()
copy code

More about array fields here.

Querying Data

Writing queries for selecting MongoDB data is easy. Querying in Djongo works exactly like in Django because Djongo translates Django’s ORM queries into MongoDB queries under the hood. You interact with your data using model managers and QuerySets, which are lazy, iterable representations of database queries.

A QuerySet is constructed when you call methods like .all() or .filter(), and only hits the database when you actually evaluate it. This design allows you to build queries incrementally without immediately hitting the database. QuerySets make it easy to reason about database access while writing clean Python code.

from blog.models import Post

# Builds a QuerySet but does not hit the database yet
qs = Post.objects.all()
copy code

Retrieving Objects

The most common methods to retrieve data are .all(), .get(), and .filter().

  • .all() returns every record as a QuerySet.

  • .get() returns a single object and raises an error if none or multiple match.

  • .filter() returns a QuerySet matching given conditions.

Filters use field lookups expressed with double underscores (e.g. field__lookup=value) and are injection safe because Django escapes parameters.

# Basic retrieval examples
post = Post.objects.get(id=1)
recent_posts = Post.objects.filter(published_date__year=2024)
all_posts = Post.objects.all()
copy code

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. Django supports lookups like exact, iexact, contains, icontains, gte, lte, in, and range.

QuerySets remain lazy until evaluated, so chaining does not hit the database immediately. This pattern allows for clean, readable, composable queries.

qs = Post.objects.filter(published_date__year=2024)\
                 .filter(title__icontains="django")\
                 .order_by('-published_date')
copy code

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 = Post.objects.order_by('-published_date')[:5]
first_post = Post.objects.order_by('published_date').first()
copy code

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)
copy code

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 = Post.objects.filter(published_date__year=2024)
print(list(qs))  # Executes the query
print(list(qs))  # Uses the cached results, no new query
copy code

Database Configuration

The settings.py supports (but is not limited to) the following options:

AttributeValueDescription
ENGINEdjongoThe MongoDB connection engine for interfacing with Django.
ENFORCE_SCHEMATrueEnsures that the model schema and database schema are exactly the same. Raises Migration Error in case of discrepancy.
ENFORCE_SCHEMAFalse(Default) Implicitly creates collections. Returns missing fields as None instead of raising an exception.
NAMEyour-db-nameSpecify your database name. This field cannot be left empty.
LOGGINGdictA dictConfig for the type of logging to run on djongo.
CLIENTdictA set of key-value pairs that will be passed directly to MongoClient as kwargs while creating a new client connection.

All options except ENGINE and ENFORCE_SCHEMA are the same those listed in the pymongo documentation.

    DATABASES = {
        'default': {
            'ENGINE': 'djongo',
            'NAME': 'your-db-name',
            'ENFORCE_SCHEMA': False,
            'CLIENT': {
                'host': 'host-name or ip address',
                'port': port_number,
                'username': 'db-username',
                'password': 'password',
                'authSource': 'db-name',
                'authMechanism': 'SCRAM-SHA-1'
            },
            'LOGGING': {
                'version': 1,
                'loggers': {
                    'djongo': {
                        'level': 'DEBUG',
                        'propagate': False,                        
                    }
                },
             },
        }
    }
copy code

Security and Integrity Checks

Djongo allows for checks on data fields before they are saved to the database. Running the correct integrity checks and field value validators before writing data into the database is important.

Enforce schema

MongoDB is schemaless, which means no schema rules are enforced by the database. You can add and exclude fields per entry and MongoDB will not complain. This can make life easier, especially when there are frequent changes to the data model. Take for example the Blog Model (version 1).

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()
copy code

You can save several entries into the DB and later modify it to version 2:

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()
    description = models.TextField()
copy code

The modified Model can be saved without running any migrations.

This works fine if you know what you are doing. Consider a query that retrieves entries belonging to both the 'older' model (with just 2 fields) and the current model. What will the value of description now be? To handle such scenarios Djongo comes with the ENFORCE_SCHEMA option.

When connecting to Djongo you can set ENFORCE_SCHEMA: True. In this case, a MigrationError will be raised when field values are missing from the retrieved documents. You can then check what went wrong.

ENFORCE_SCHEMA: False works by silently setting the missing fields with the value None. If your app is programmed to expect this (which means it is not a bug) you can get away by not calling any migrations.

Validators

Apply validators to each field before they are saved.

from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from djongo import models
from django.core.validators import URLValidator

def script_injection(value):
    if value.find('<script>') != -1:
        raise ValidationError(_('Script injection in %(value)s'),
                              params={'value': value})

class Address(models.Model)
    city = models.CharField(max_length=50)
    homepage = models.URLField(validators=[URLValidator, script_injection])
    class Meta:
        abstract=True

class Entry(models.Model):
    _id = models.ObjectIdField()
    address = models.EmbeddedField(model_container=Address)
copy code

Integrity checks

class Entry(models.Model):
    _id = models.ObjectIdField()
    address = models.EmbeddedField(model_container=Address,
                                   null=False,
                                   blank=False)
copy code

By setting null=False, blank=False in EmbeddedField, missing values are never stored.

Djongo Manager

Djongo Manager extends the functionality of the usual Django Manager. It gives direct access to the pymongo collection API. To use this manager define your manager as DjongoManager in the model.

class Entry(models.Model):
    blog = models.EmbeddedField(
        model_container=Blog,
    )
    headline = models.CharField(max_length=255)    
    objects = models.DjongoManager()
copy code

Use it like the usual Django manager:

post = Entry.objects.get(pk=p_key)
copy code

Will get a model object having primary key p_key.

Using PyMongo Commands

MongoDB has powerful query syntax and DjongoManager lets you exploit it fully. For the above Entry model define a custom query function:

class EntryView(DetailView):

    def get_object(self, queryset=None):
        index = [i for i in Entry.objects.mongo_aggregate([
            {
                '$match': {
                    'headline': self.kwargs['path']
                }
            },
        ])]

        return index
copy code

You can directly access any pymongo command by prefixing mongo_ to the command name.

For example, to perform aggregate on the BlogPage collection (BlogPage is stored as a table in SQL or a collection in MongoDB) the function name becomes mongo_aggregate. To directly insert a document (instead of .save() a model) use mongo_insert_one()

GridFS

To save files using GridFS you must create a file storage instance of GridFSStorage:

grid_fs_storage = GridFSStorage(collection='myfiles')
copy code

In your model define your field as FileField or ImageField as usual:

avatar = models.ImageField(storage=grid_fs_storage, upload_to='')
copy code

Refer to Using GridFSStorage for more details.

Latest Updates

  1. Start by creating an account. You will be assigned a working webserver instance running Django and MongoDB.
  2. Once logged into the dashboard click on generate token. This generates and installs a pass token needed for downloading the latest version of djongo for local development.
  3. Copy and add the generated token into the ~/.netrc file located in your local machines home directory.
machine pypi.djongomapper.com
login <djongocs username>
password <generated token>
copy code

You can then install the latest version of djongo:

pip install --extra-index-url https://pypi.djongomapper.com/latest-updates/ djongo
copy code

Make sure to safely install and store the pass token inside the ~/.netrc file. The pass token is not saved on the DjongoCS server locally.

Contribute

If you think djongo is useful, please share it with the world! Your endorsements and online reviews will help get more support for this project.

You can contribute to the source code or the documentation by creating a simple pull request! You may want to refer to the design documentation to get an idea on how Django MongoDB connector is implemented.