How to Build Modern Web Applications with Django and MongoDB?

Last Updated : 7 Feb, 2026

In February 2025, MongoDB announced something the Python community had been waiting for: an official Django MongoDB backend. No more hacky workarounds, no more third-party transpilers with limited support - just native, first-class MongoDB integration with Django's beloved ORM.

Why Django + MongoDB?

Django has long been the go-to framework for Python developers who want to build web applications quickly without sacrificing quality. Its "batteries included" philosophy, robust ORM, and automatic admin interface have made it a favorite for projects of all sizes. MongoDB, on the other hand, has emerged as the leading NoSQL database, offering:

  • Flexible Schema Design: Store documents without rigid table structures.
  • Horizontal Scalability: Scale out across commodity hardware.
  • High Availability: Built-in replication and automatic failover.
  • Developer Friendliness: The document model naturally mirrors how developers think about data in code.

When combined, Django and MongoDB offer a compelling stack:

  • Rapid Development: Django's conventions combined with MongoDB's flexibility mean you spend less time on boilerplate and more time building features.
  • Schema Evolution: Modify data structures without painful migrations. MongoDB's flexible schema lets your data model evolve with your application.
  • Performance: MongoDB's indexing capabilities and powerful aggregation pipeline deliver excellent query performance.
  • Full-Stack Django: Keep using everything you love about Django: admin, auth, forms, and templates all work seamlessly.

The Evolution: From PyMongo to Official Backend

Before the official backend, developers had three primary options for connecting Django to MongoDB:

1. PyMongo (Direct Driver)

The official Python driver for MongoDB. While powerful, it bypassed Django's ORM entirely:

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
db = client['mydb']
collection = db['users']
# No Django ORM benefits
result = collection.find_one({'email': 'user@example.com'})
  • Pros: Full MongoDB feature access, official support
  • Cons: No Django ORM integration, manual everything

2. MongoEngine

A document-object mapper that provides an ORM-like experience:

from mongoengine import Document, StringField
class User(Document):
email = StringField(required=True)
name = StringField(max_length=100)
  • Pros: ORM-like syntax, good abstraction
  • Cons: Not Django's native ORM, compatibility issues with Django features

3. Djongo

A SQL-to-MongoDB transpiler that attempted to make MongoDB work with Django's ORM:

# settings.py
DATABASES = {
'default': {
'ENGINE': 'djongo',
'NAME': 'mydb',
}
}
  • Pros: Uses Django's native ORM syntax
  • Cons: Performance overhead from SQL translation, limited feature support, maintenance concerns

The Official Backend: A Game Changer

Enter django-mongodb-backend-MongoDB's official solution that provides:

  • Native Django ORM support without SQL translation.
  • Official MongoDB maintenance and support.
  • Full Django ecosystem compatibility (admin, auth, migrations).
  • A modern aggregation pipeline instead of SQL-style queries.
  • Active development with a clear roadmap.

Getting Started

1. Prerequisites

Before you begin, ensure you have:

  • Python 3.10 or higher.
  • Django 5.0+ (version must match the backend version).
  • MongoDB 5.0+ (or a MongoDB Atlas account).

2. Installation

Install the package matching your Django version:

For Django 5.2

pip install django-mongodb-backend==5.2.*

For Django 5.1

pip install django-mongodb-backend==5.1.*

For Django 5.0

pip install django-mongodb-backend==5.0.*

3. Creating a New Project

MongoDB provides an official project template that comes pre-configured:

Create project using the official template

django-admin startproject myproject \
--template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/5.2.x.zip
cd myproject

This template includes:

  • Pre-configured settings.py for MongoDB.
  • The proper project structure.
  • Example configurations.

4. Existing Project Migration

For existing Django projects, update your settings.py:

DATABASES = {
"default": {
"ENGINE": "django_mongodb_backend",
"HOST": "mongodb://localhost:27017",
"NAME": "your_database_name",
},
}

Configuration Deep Dive

1. Basic Configuration

The minimal configuration requires three settings:

DATABASES = {

"default": {

"ENGINE": "django_mongodb_backend",

"HOST": "mongodb://localhost:27017",

"NAME": "myapp_db",

},

}

2. MongoDB Atlas Configuration

For cloud deployments with MongoDB Atlas:

DATABASES = {

"default": {

"ENGINE": "django_mongodb_backend",

"HOST": "mongodb+srv://username:password@cluster.xxxxx.mongodb.net/?retryWrites=true&w=majority",

"NAME": "production_db",

},

}

3. Advanced Connection Options

For more control, use the full connection string URI format:

C++
import os
DATABASES = {
    "default": {
        "ENGINE": "django_mongodb_backend",
        "HOST": os.environ.get("MONGODB_URI", "mongodb://localhost:27017"),
        "NAME": os.environ.get("MONGODB_NAME", "development_db"),
        "OPTIONS": {
            # PyMongo connection options
            "maxPoolSize": 50,
            "minPoolSize": 10,
            "maxIdleTimeMS": 30000,
            "connectTimeoutMS": 5000,
            "serverSelectionTimeoutMS": 5000,
            "retryWrites": True,
            "w": "majority",
        },
    },
}

Keep sensitive data out of your code:

settings.py

C++
import os
from pathlib import Path


DATABASES = {
    "default": {
        "ENGINE": "django_mongodb_backend",
        "HOST": os.environ.get("MONGODB_HOST"),
        "NAME": os.environ.get("MONGODB_NAME"),
    },
}


And in your .env file (never commit this!):

MONGODB_HOST=mongodb+srv://user:pass@cluster.mongodb.net

MONGODB_NAME=myapp_production

Working with Models

1. Defining Models

Django models work as expected with the MongoDB backend:

models.py

C++
from django.db import models
class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField(unique=True)
    bio = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    def __str__(self):
        return self.name


class Article(models.Model):
    title = models.CharField(max_length=300)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='articles')
    published = models.BooleanField(default=False)
    published_at = models.DateTimeField(null=True, blank=True)
    tags = models.JSONField(default=list)  # Store arrays naturally!
    metadata = models.JSONField(default=dict)  # Store nested objects!
    views = models.PositiveIntegerField(default=0)


    class Meta:
        ordering = ['-published_at']
        indexes = [
            models.Index(fields=['slug']),
            models.Index(fields=['published', '-published_at']),
        ]


    def __str__(self):
        return self.title

2. Leveraging MongoDB's Document Model

One of MongoDB's strengths is storing nested data. Use JSONField to store complex structures:

C++
class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)


    # Store specifications as nested document
    specifications = models.JSONField(default=dict)
    # Example: {"weight": "1.5kg", "dimensions": {"width": 10, "height": 20}}


    # Store variants as array of documents
    variants = models.JSONField(default=list)
    # Example: [{"color": "red", "stock": 50}, {"color": "blue", "stock": 30}]


    # Store category hierarchy
    categories = models.JSONField(default=list)
    # Example: ["Electronics", "Computers", "Laptops"]

3. Model Relationships

Standard Django relationships work with the MongoDB backend:

C++
# One-to-Many
class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    author_name = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
# Many-to-Many
class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)
class Post(models.Model):
    title = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag, related_name='posts')

Querying Data

1. Standard Django QuerySet API

The familiar Django ORM syntax works out of the box:

C++
# Get all published articles
published_articles = Article.objects.filter(published=True)
# Get articles by a specific author
author_articles = Article.objects.filter(author__name='John Doe')
# Complex queries
from django.db.models import Q
results = Article.objects.filter(
    Q(title__icontains='python') | Q(tags__contains=['programming']),
    published=True
).order_by('-published_at')[:10]

2. Field Lookups

Standard Django lookups are converted to MongoDB aggregation operations:

C++
# Exact match
Article.objects.filter(slug='my-article')
# Case-insensitive contains
Article.objects.filter(title__icontains='django')
# Greater than / Less than
Article.objects.filter(views__gt=1000)
Article.objects.filter(published_at__lt=datetime.now())
# In list
Article.objects.filter(author_id__in=[1, 2, 3])
# Range
from datetime import datetime, timedelta
last_week = datetime.now() - timedelta(days=7)
Article.objects.filter(published_at__range=[last_week, datetime.now()])

3. Aggregations

Django's aggregation framework is fully supported:

C++
from django.db.models import Count, Avg, Sum, Max, Min


# Count articles per author
Author.objects.annotate(article_count=Count('articles'))


# Average views per article
from django.db.models import Avg
average_views = Article.objects.aggregate(Avg('views'))


# Multiple aggregations
stats = Article.objects.aggregate(
    total_views=Sum('views'),
    avg_views=Avg('views'),
    max_views=Max('views'),
    article_count=Count('id')
)`

4. Raw Aggregation Pipelines

For advanced queries, you can use MongoDB's aggregation pipeline directly:

C++
from django_mongodb_backend.expressions import RawAggregation


# Access the raw collection for complex aggregations
from django.db import connection


collection = connection.get_collection('myapp_article')
pipeline = [
    {'$match': {'published': True}},
    {'$group': {
        '_id': '$author_id',
        'total_views': {'$sum': '$views'},
        'article_count': {'$sum': 1}
    }},
    {'$sort': {'total_views': -1}},
    {'$limit': 10}
]
results = list(collection.aggregate(pipeline))

Migrations and Admin

1. Running Migrations

Django migrations work with MongoDB:

# Create migrations
python manage.py makemigrations

# Apply migrations
python manage.py migrate

# Check migration status
python manage.py showmigrations

2. Django admin

The admin interface is fully functional:

C++
# admin.py
from django.contrib import admin
from .models import Author, Article


@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
    list_display = ['name', 'email', 'created_at']
    search_fields = ['name', 'email']



@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'published', 'views', 'published_at']
    list_filter = ['published', 'author']
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'published_at'

3. Creating a Superuser

python manage.py createsuperuser

Dockerizing Django-MongoDB Application

Now, for the exciting part: containerizing your application for consistent development and deployment environments.

Project structure

myproject/

├── docker/

│ ├── Dockerfile

│ └── docker-compose.yml

├── myproject/

│ ├── __init__.py

│ ├── settings.py

│ ├── urls.py

│ └── wsgi.py

├── myapp/

│ ├── __init__.py

│ ├── models.py

│ ├── views.py

│ └── admin.py

├── requirements.txt

├── manage.py

└── .env.example

1. Docker File

C++
# Dockerfile
FROM python:3.12-slim


# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1


# Set work directory
WORKDIR /app


# Install system dependencies
RUN apt-get update && apt-get install -y \
    gcc \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*


# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt


# Copy project files
COPY . .


# Create non-root user for security
RUN adduser --disabled-password --gecos '' appuser && \
    chown -R appuser:appuser /app
USER appuser


# Expose port
EXPOSE 8000


# Run the application
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "myproject.wsgi:application"]

2. Docker Compose Configuration

C++
# docker-compose.yml
version: '3.8'


services:
  # MongoDB Service
  mongodb:
    image: mongodb/mongodb-atlas-local:latest
    container_name: mongodb
    ports:
      - "27017:27017"
    volumes:
      - mongodb_data:/data/db
    environment:
      - MONGODB_INITDB_ROOT_USERNAME=admin
      - MONGODB_INITDB_ROOT_PASSWORD=password123
    healthcheck:
      test: echo 'db.runCommand("ping").ok' | mongosh mongodb://localhost:27017 --quiet
      interval: 10s
      timeout: 10s
      retries: 5
      start_period: 40s
    networks:
      - app-network


  # Django Application
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: django_app
    ports:
      - "8000:8000"
    volumes:
      - .:/app
      - static_volume:/app/staticfiles
    environment:
      - DEBUG=1
      - SECRET_KEY=your-secret-key-here
      - MONGODB_HOST=mongodb://admin:password123@mongodb:27017
      - MONGODB_NAME=django_db
      - ALLOWED_HOSTS=localhost,127.0.0.1
    depends_on:
      mongodb:
        condition: service_healthy
    command: >
      sh -c "python manage.py migrate &&
             python manage.py runserver 0.0.0.0:8000"
    networks:
      - app-network


  # Nginx (for production)
  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - static_volume:/app/staticfiles
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - web
    networks:
      - app-network
    profiles:
      - production


volumes:
  mongodb_data:
  static_volume:


networks:
  app-network:
    driver: bridge

3. Requirements File

C++
# requirements.txt
Django>=5.2,<5.3
django-mongodb-backend==5.2.*
gunicorn==21.2.0
python-dotenv==1.0.0
whitenoise==6.6.0

4. Environment configuration

C++
# settings.py - Docker-aware configuration
import os
from pathlib import Path


BASE_DIR = Path(__file__).resolve().parent.parent


SECRET_KEY = os.environ.get('SECRET_KEY', 'development-secret-key')
DEBUG = os.environ.get('DEBUG', '0') == '1'
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost').split(',')


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp',
]


MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # For static files in production
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


ROOT_URLCONF = 'myproject.urls'


TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]


WSGI_APPLICATION = 'myproject.wsgi.application'


# MongoDB Database Configuration
DATABASES = {
    "default": {
        "ENGINE": "django_mongodb_backend",
        "HOST": os.environ.get("MONGODB_HOST", "mongodb://localhost:27017"),
        "NAME": os.environ.get("MONGODB_NAME", "django_db"),
    },
}


# Default primary key field type
DEFAULT_AUTO_FIELD = "django_mongodb_backend.fields.ObjectIdAutoField"


# Static files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'


# Internationalization
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True

5. Running the Application

C++
# Build and start all services
docker-compose up --build


# Run in detached mode
docker-compose up -d --build


# View logs
docker-compose logs -f web


# Run migrations
docker-compose exec web python manage.py migrate


# Create superuser
docker-compose exec web python manage.py createsuperuser


# Stop all services
docker-compose down


# Stop and remove volumes (warning: deletes data)
docker-compose down -v

6. Development vs production Docker Compose

Create a separate file for production:

C++
# docker-compose.prod.yml
version: '3.8'

services:
  mongodb:
    restart: always
    environment:
      - MONGODB_INITDB_ROOT_USERNAME=${MONGO_USER}
      - MONGODB_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}

  web:
    restart: always
    environment:
      - DEBUG=0
      - SECRET_KEY=${SECRET_KEY}
      - MONGODB_HOST=mongodb://${MONGO_USER}:${MONGO_PASSWORD}@mongodb:27017
      - ALLOWED_HOSTS=${ALLOWED_HOSTS}
    command: gunicorn --bind 0.0.0.0:8000 --workers 4 myproject.wsgi:application


  nginx:
    restart: always
    profiles: []  # Enable nginx in production


Run production:

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

7. Windows-Specific Considerations

If you're developing on Windows with Docker Desktop, note that MongoDB's memory-mapped files don't work well with VirtualBox shared folders. Use Docker volumes instead of bind mounts for MongoDB data:

# Don't do this on Windows:
volumes:
- ./mongo-data:/data/db # Problematic on Windows


# Do this instead:
volumes:
- mongodb_data:/data/db # Named volume works fine

Production Considerations

1. Security Best Practices

  1. Use MongoDB Atlas in production: Built-in security, backups, and scaling
  2. Enable authentication: Always use username/password or certificate auth
  3. Network isolation: Use private networks and VPCs
  4. Encrypt at rest and in transit: TLS for connections, encryption for storage
C++
# Production settings
DATABASES = {
    "default": {
        "ENGINE": "django_mongodb_backend",
        "HOST": os.environ["MONGODB_URI"],  # Use Atlas connection string
        "NAME": os.environ["MONGODB_NAME"],
        "OPTIONS": {
            "tls": True,
            "tlsCAFile": "/path/to/ca-certificate.pem",
            "retryWrites": True,
            "w": "majority",
        },
    },
}

2. Performance Optimization

  1. Create proper indexes: Define indexes in your models
  2. Use connection pooling: Configure pool size based on load
  3. Enable query profiling: Monitor slow queries
C++
# Model with optimized indexes
class Article(models.Model):
    title = models.CharField(max_length=300, db_index=True)
    slug = models.SlugField(unique=True)
    published = models.BooleanField(default=False, db_index=True)


    class Meta:
        indexes = [
            models.Index(fields=['published', '-created_at']),
            models.Index(fields=['author', '-created_at']),
        ]

3. Monitoring

Use MongoDB Atlas monitoring or set up your own with:

  • MongoDB Compass for visual exploration.
  • Application Performance Monitoring (APM).
  • Log aggregation for Django application logs.

Common Pitfalls and Solutions

1. Version Mismatch

Problem: ImportError or compatibility errors

Solution: Always match django-mongodb-backend version with your Django version:

Check Django version

python -c "import django; print(django.__version__)"

Install matching backend

pip install django-mongodb-backend==5.2.* # For Django 5.2.x

2. ObjectId vs Integer Primary Keys

Problem: Django expects integer primary keys by default

Solution: Use ObjectIdAutoField:

settings.py

DEFAULT_AUTO_FIELD = "django_mongodb_backend.fields.ObjectIdAutoField"

3. JOIN-Heavy Queries

Problem: Slow performance with many relationships

Solution: MongoDB uses $lookup for JOINs, which can be slow. Consider:

  • Embedding related data in JSONField.
  • Denormalizing frequently accessed data.
  • Using select_related() and prefetch_related().

4. Transaction Support

Problem: Transactions behave differently

Solution: MongoDB supports multi-document transactions, but they have overhead. Design your data model to minimize transaction needs.

Problem: Full-text search not working as expected

Solution: Create text indexes in MongoDB:

After migration, create text index

from django.db import connection

collection = connection.get_collection('myapp_article')

collection.create_index([('title', 'text'), ('content', 'text')])

What's Coming Next

The General Availability release planned for later in 2025 will include:

  • BSON data type support: Native handling of MongoDB's data types.
  • Embedded document support: First-class support for nested documents in arrays.
  • Enhanced aggregation: More aggregation pipeline operators.
  • Performance improvements: Optimized query translation.

The official Django MongoDB Backend represents a significant milestone for Python developers. It brings together the best of both worlds:

  • Django's rapid development philosophy and rich ecosystem
  • MongoDB's flexible, scalable document database

With Docker integration, you can now:

  • Develop consistently across team members.
  • Deploy with confidence to any environment.
  • Scale your application as your needs grow.
  • The combination of Django's "batteries included" approach with MongoDB's document model creates a powerful stack for modern web applications-whether you're building a simple blog or a complex enterprise system.

To Start:

Create a new project

pip install django-mongodb-backend==5.2.*

django-admin startproject myproject \

--template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/5.2.x.zip

Or add to an existing project

pip install django-mongodb-backend==5.2.*

Update settings.py with MongoDB configuration

Comment
Article Tags:

Explore