Tutorials Web Development

How to Connect Django With MongoDB

Read this post in other languages:

One of the most popular web frameworks, Django, has adopted the “batteries included” philosophy. This means that you can build a production-ready application using only the vanilla Django with no extensions or other frameworks. Even the database SQLite is available out of the box. SQLite is great for learning and developing small applications, but has certain limitations that often lead to issues as projects grow.

Developers tend to choose other databases over SQLite so that their apps can be scaled and perform high-load operations. Django officially supports a number of databases, including PostgreSQL, MariaDB, MySQL, Oracle, and SQLite, as well as some third-party database backends. MongoDB is not on the list, but it is still a very popular option: 8% of Django developers use it, according to the Django Developers Survey conducted by JetBrains in 2023.

Database backends used by Django developers

Who needs this and why?

What are the possible reasons for choosing MongoDB for Django projects and what does Django’s lack of official support for this database mean in practice? Let’s see.

All of the officially supported databases are relational database management systems (RDBMS), meaning that they store data in tables and support SQL (Structured Query Language). MongoDB, however, is a non-relational database. Such databases are also often referred to as NoSQL. Instead of tables with rows and columns, they store data in a JSON-like format called BSON.

SQL features vs MongoDB features

As a non-relational database, MongoDB has the following main advantages over SQL databases:

  • No schema: The flexibility of BSON can be very helpful if you need to store complex data whose structure is likely to require changes over time.
  • Document model: The format of data can vary from document to document, which is ideal for hierarchical data storage and various unstructured data.
  • Scalability: MongoDB is horizontally scalable by design and supports sharding for handling large volumes of data and high loads.
  • Real-time processing: With MongoDB, applications can react to data changes almost instantly by subscribing to change streams.

Considering these advantages, developers often prefer MongoDB for blogging platforms and applications that deal with user-generated content, IoT (Internet of Things), Big Data services, and mobile applications.

Usages of MongoDB

It’s important to understand, however, that MongoDB cannot serve as a drop-in replacement for SQL databases. Since there’s no native support for MongoDB in Django, you’ll have to integrate third-party packages and adjust the codebase accordingly. Moreover, you’ll have to organize the whole development process, taking into account the following limitations:

  • Django’s built-in SQL-oriented ORM becomes useless with MongoDB.
  • You will no longer be able to manage the content through the Django admin interface.
  • Many other Django features designed for working with databases will be out of reach. By using MongoDB, you’ll definitely step off “the Django way”.

The more code you already have, the more changes will be required. For this reason, it makes sense to switch to MongoDB in the very early stages of your project’s lifecycle.

In this tutorial, we will connect a very basic Django project with MongoDB in two different ways:

  • By using MongoEngine (which is based on PyMongo)
  • By using only PyMongo

Requirements

Before your start, you need to install the following:

The installation instructions for the following Python packages and software will be provided in the course of the tutorial:

Prepare your Django project

We will use a project with a basic Django to-do list application. If you want to create the project from scratch, you can follow this tutorial in PyCharm’s documentation. Otherwise, just clone the repository as follows:

  1. Launch PyCharm and then click Get from VCS on the Welcome screen.
  2. Insert the repository URL and click Clone.

PyCharm downloads the project and configures your system interpreter. In this tutorial, we will use a local virtualenv interpreter, which you’ll also need to set up.

Setting up the project interpreter

Click Configure a Python interpreter in the popup that appears right after you clone the project. Alternatively, click the interpreter selector in the lower right-hand corner, and then select Add New Interpreter | Add Local Interpreter. For more information about configuring interpreters in PyCharm, refer to the documentation.

After you’ve configured the interpreter, open the Python Packages tool window and install Django.

Installing Django in the Python Packages tool window

Install MongoDB

Now that you have your Django project ready, you need to provision the MongoDB database. You can use one of three options, depending on what suits your project best:

Set up MongoDB Atlas

If you’re going to connect your Django project with a cloud MongoDB database, sign up for MongoDB Atlas and deploy a free database cluster there. To access this cluster from your application, you also need to add your connection IP address to the IP access list and create a database user. For the next steps of this tutorial, you’ll need the connection string. Here’s how you can get it:

  1. Select your database.
  2. Click Connect on the Overview tab.
  3. Select Drivers.
Connecting to the cloud database

For more detailed information, see the MongoDB Atlas documentation.

Run MongoDB in a Docker container

If you decide to run MongoDB Community Edition in a container, follow these steps:

  1. Pull the MongoDB Docker image:
docker pull mongodb/mongodb-community-server
  1. Run the image as a container:
docker run --name mongo -d -p 27017:27017 mongodb/mongodb-community-server:latest

For further details, refer to the MongoDB documentation.

Install MongoDB locally

If you’re opting to run MongoDB as a local database, perform these steps:

  1. Install MongoDB Community Edition.
  2. Start the MongoDB Shell and create a database:
% mongosh
> use djangoTutorial

Configure the data source

This step is optional. Configuring a data source allows you to view the database collections and track changes to them right in PyCharm rather than install additional software or open the web interface of MongoDB Atlas.

Go back to PyCharm, open the Database tool window, and click “+” to start creating a data source. Select MongoDB as the data source type.

Creating a MongoDB data source

Configure the newly created data source. If you are using MongoDB Atlas, insert the connection string into the URL field, and then provide the credentials of the database user.

Configuring the data source for MongoDB Atlas

If you’re using MongoDB Community, either locally installed or running in a Docker container, configure the data source as follows:

  1. If you’ve changed the default settings during the installation, enter the database host address and port number. Otherwise, proceed to step 2.
  2. If you’ve configured the username and the password, enter them in the corresponding fields. Otherwise, select No auth in Authentication.
  3. Specify the database name in the Database field.
  4. Click Test Connection.
Configuring the data source and testing connection

For more information about how to configure a MongoDB data source in PyCharm, refer to the documentation.

Configure environment variables

If you’re using MongoDB Atlas or a local MongoDB database with user and password authentication, you need to create an .env file with the environment variables to store your credentials. This is considered a best practice for preventing leaks of confidential data when putting projects under version control.

  1. Right-click the root directory of your project and select New | File from the context menu.
  2. Specify .env as the file name.
  3. The newly created file is opened in the editor. Add the environment variables and their values. For example:
USERNAME=jetbrains
PASSWORD=czUfHKhGNxtGTsv
HOST=cluster0.bqnh3eo.mongodb.net

HOST is required only for MongoDB Atlas.

  1. Right-click anywhere in the editor and select Git | Add to .gitignore from the context menu.

Next, you need to configure the run configuration so that the newly created .env file is loaded every time you start the Django server.

  1. Click the Run widget above and select Edit Configurations from the menu.
  2. Specify the path to .env in the Paths to “.env” files field.
Specify the path to .env file in the run configuration
  1. Click OK or Run when you’re ready.

Now you are all set to proceed with the selected method for connecting Django and MongoDB (or try both).

Connect Django with MongoDB using MongoEngine

MongoEngine is a Python Object-Document Mapper (ODM) for Django projects. It works similarly to ORM (Object-relational mapping) in relational databases. Here are the main advantages of connecting MongoDB to Django through MongoEngine:

  • ODM allows defining schemas for documents in code with familiar syntax (similar to models when using Django with SQL databases).
  • Rich “pythonic” syntax for database queries.
  • Support for Django forms and Django Rest Framework serializers.

To sum up, MongoEngine provides a high-level experience of interaction with MongoDB in Django projects. It can also be a great choice if you need to convert an existing Django project to one using MongoDB.

Install and enable MongoEngine

Let’s start by installing the MongoEngine package in the Python Packages tool window. Open the tool window, type mongoengine, click Install, and select the version.

Installing mongoengine in the Python Packages tool window

By default, Django projects are created with an SQLite database. We need to disable it by commenting (or removing) the DATABASES section in settings.py. Press ⌥⌘O / Ctrl+Alt+Shift+N and start typing DA… for quick access.

Quick navigation to the DATABASES section in settings.py

Instead, add the following to settings.py:

import mongoengine
mongoengine.connect(db="djangoTutorial", host="mongodb://localhost:27017/")
Disabling the SQLite connection parameters in settings.py

For MongoDB Atlas, load the username and the password from the .env file. For example:

import os
import mongoengine

USERNAME = os.getenv("USERNAME")
PASSWORD = os.getenv("PASSWORD")
HOST = os.getenv("HOST")


mongoengine.connect(db="djangoTutorial", host=f"mongodb+srv://{HOST}/",
                    username=USERNAME, password=PASSWORD)

Edit the model

Although MongoDB is schemaless, MongoEngine allows defining schemas at the application level. Such schemas are not passed to the database, but they let developers write more concise and maintainable code by adding an extra level of abstraction to the interaction between Django and MongoDB.

Unlike the Django ORM, MongoEngine uses documents instead of models. Unfortunately, MongoEngine documents are not supported by the Django admin interface. To avoid errors when launching the Django server, unregister ToDoItem in admin.py by commenting or removing the corresponding class.

You can use the Django Structure tool window for quick access to the model admin class:

Quick access via the Django Structure tool window

Double-click ToDoItem under Models in the Django Structure tool window to open the model in the editor. Make the following replacements:

OriginalReplacement
from django.db import modelsfrom mongoengine import Document, fields

or

from mongoengine import *
models.ModelDocument
models.CharFieldfields.StringField()
models.DateFieldfields.DateField

Here’s what you should get:

from mongoengine import Document, fields
from django.utils import timezone


class ToDoItem(Document):
    text = fields.StringField(max_length=100)
    due_date = fields.DateField(default=timezone.now)

    def __str__(self):
        return f"{self.text}: due {self.due_date}"

To avoid making replacements manually, you can ask Pycharm’s AI Assistant to do this for you:

  1. Select the code in models.py.
  2. From the context menu, select AI Actions | New Chat Using Selection.
  3. Write the prompt, for example, Modify the code for usage with MongoEngine.
Modifying the code by using AI Assistant

For information about using AI Assistant, refer to the PyCharm documentation.

Now let’s test the solution by creating an instance of the ToDoItem document and saving it to the database. Open the Python console and run the following:

from todo.models import ToDoItem
from datetime import datetime
task = ToDoItem(text="Fix the table")
task.due_date = datetime(2023,11,29)
task.save()

If you are using a cloud database, this may take a while. In the event you get an [SSL: CERTIFICATE_VERIFY_FAILED] error, see the troubleshooting recommendations.

Open the to_do_item collection in the Database tool window and make sure that the record has been added:

The first record in the MongoDB database

Let’s make sure that our changes didn’t break the application. Launch the Django server configuration, which was automatically created by PyCharm.

Launching the Django server configuration

Then, go to http://localhost:8000/todo/ in the browser:

Application page in browser

Congratulations! Your Django app is using MongoDB now!

Connect Django with MongoDB using PyMongo

PyMongo is the officially recommended low-level driver for MongoDB. It provides a direct and detailed way of interaction between Django and MongoDB. PyMongo is a great choice if you need to craft database queries for better performance and can do without the ORM-like experience in your codebase.

Install PyMongo and remove the model

MongoEngine runs PyMongo under the hood. So, if you’ve just gone through the previous part of the tutorial, you already have pymongo installed in the project environment. Otherwise, install it in the Python Packages tool window.

Installing pymongo in the Python Packages tool window

Disable SQLite by commenting the DATABASES section in settings.py and remove ToDoItemAdmin from admin.py if you haven’t done so yet.

Finally, remove ToDoItem from models.py. You can use Recent Files (⌘E / Ctrl+E) for quick access.

Transform the views

As we are not using Django models anymore, we should change the view classes accordingly. Go to views.py and add a function that will be used to access the MongoDB database:

from pymongo import MongoClient


def get_db_handle():
    client = MongoClient(host="mongodb://localhost", port=27017)
    db_handle = client.djangoTutorial.to_do_item
    return db_handle

For MongoDB Atlas or a local database with authentication, you need to load environment variables and use the connection string. For example:

from pymongo import MongoClient
import os

USERNAME = os.getenv("USERNAME")
PASSWORD = os.getenv("PASSWORD")
HOST = os.getenv("HOST")


def get_db_handle():
    client = MongoClient(f"mongodb+srv://{USERNAME}:{PASSWORD}@{HOST}/?retryWrites=true&w=majority")
    db_handle = client.djangoTutorial.to_do_item
    return db_handle

The function returns the to_do_item MongoDB collection. One of MongoDB’s advantages is that even if this collection doesn’t exist yet, it will be created on the first insert.

As we don’t have the ToDoItem model anymore, we need to update the get_queryset methods of the view classes AllToDos and TodayToDos:

class AllToDos(ListView):
    template_name = "todo/index.html"

    def get_queryset(self):
        db = get_db_handle()
        results = db.find()
        return results


class TodayToDos(ListView):
    template_name = "todo/today.html"

    def get_queryset(self):
        db = get_db_handle()
        today = datetime.combine(date.today(), time())
        results = db.find({"due_date": today}).sort("due_date")
        return results

By changing the code in this way, we are overriding the default get_queryset method of Django’s ListView class. Here’s what this code does:

The AllToDos class:

  • Calls get_db_handle to get the to_do_item collection from the djangoTutorial database and assigns it to db (line 5).
  • Applies the find() method of PyMongo’s collections to fetch all of the items in the collection (line 6).

The TodayToDos class:

  • Sets the current date combined with the default time (midnight) as today. We don’t need time in our application, but we can’t get rid of it because it is required by the BSON format (line 15).
  • Fetches all items whose due_date is today and sorts them by due_date (line 16).

Don’t forget to update the imports. Hover over datetime and time(), which should be highlighted with a red squiggly line, and then select Import this name. Import from datetime in both cases.

Updating imports by using a quick-fix

If the Django server is not running, launch it, and then go to http://localhost:8000/todo/ in the browser to make sure that the application works.

Continue developing

At the moment we can add records to the database via the Python console. However, the console isn’t available for the users of our application. It looks like we should develop a view that will allow adding new records to the database in the browser.

Let’s start with writing a web form. Create a forms.py file in the application directory (todo) and fill it with the following code:

from django import forms
from datetime import datetime


class ToDoItemForm(forms.Form):
    text = forms.CharField(max_length=100)
    due_date = forms.DateField(initial=datetime.now)

The form consists of two fields: text and due_date. The latter defaults to the current date.

Now let’s add a view to views.py. At the beginning of this tutorial, we already mentioned that many Django shortcuts will no longer work if you decide to use Django with MongoDB. This time you’ll have to use a function-based view and manually define some basic logic (which would have been simply inherited via class-based views had we used a different database).

If you’ve connected Django to MongoDB with PyMongo, the function code should be as follows:

from django.shortcuts import redirect, render
from .forms import ToDoItemForm
...

def add_todo(request):
    if request.method == "POST":
        form = ToDoItemForm(request.POST)
        if form.is_valid():
            new_todo = {
                "text": form.cleaned_data["text"],
                "due_date": datetime.combine(form.cleaned_data["due_date"], time())
            }
            db = get_db_handle()
            db.insert_one(new_todo)
            return redirect('today')
    else:
        form = ToDoItemForm()

    return render(request, "todo/add.html", {"form": form})

Here’s the breakdown:

  1. If the add_todo view receives a POST request, it assigns the ToDoItemForm data to form (lines 6–7).
  2. It first ensures the posted form data is valid, and then creates a dictionary from the form data (lines 8–12).
  3. It connects to MongoDB via the get_db_handle method, and inserts a new record into the database (lines 13–14).
  4. If it’s not a POST request, it simply returns a blank form (line 16).

If you’re using MongoEngine, the code will be shorter as you can use the built-in save() method available in MongoEngine documents:

from django.shortcuts import redirect, render
from .forms import ToDoItemForm
...

def add_todo(request):
    if request.method == "POST":
        form = ToDoItemForm(request.POST)
        if form.is_valid():
            new_todo = ToDoItem(text=form.cleaned_data["text"], due_date=form.cleaned_data["due_date"])
            new_todo.save()
            return redirect("today")
    else:
        form = ToDoItemForm()

    return render(request, "todo/add.html", {"form": form})

The reference to the template file add.html in the return statement should be highlighted with a yellow squiggly line. Hover over it and select Create template todo/add.html.

Creating a Django template by using the quick-fix

The template will open in the editor. Fill it with the following HTML code:

{% extends "base.html" %}
{% block content %}
    <h1>Add new to-do:</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.text.label_tag }} {{ form.text }}
        <br>
        {{ form.due_date.label_tag }} {{ form.due_date }}
        <br>
        <button type="submit">Submit</button>
    </form>
{% endblock %}

Update base.html by adding a button for creating new tasks:

<div>
    <a class="button" href='{% url "index" %}'>All tasks</a>
    <a class="button" href='{% url "today" %}'>Today</a>
    <a class="button" href='{% url "add" %}'>Add</a>
</div>

Update todo/urls.py to allow yourself to access the page for adding tasks in the browser:

urlpatterns = [
    path("", views.AllToDos.as_view(), name="index"),
    path("today/", views.TodayToDos.as_view(), name="today"),
    path("add/", views.add_todo, name="add")
]

Finally, it’s time to test the new feature in the browser! Go to http://localhost:8000/todo/add/ and add a task:

Adding a task in the browser

Conclusion

If you’ve followed all of the steps in this tutorial, you have successfully connected a basic Django project with MongoDB by using two libraries: PyMongo and MongoEngine.

As you already know, MongoEngine is actually based on PyMongo. So, what’s the difference, and how do you choose the right way for your Django project? We’ve summarized the advantages and disadvantages of each method in the following table:

MongoEnginePyMongo
Access to collectionsDocument classes are mapped to collectionsCollections are accessed through MongoClient
QueryingChained queries: ToDoItem.objects.filter(due_date=date.today())Dictionary style queries: db.find({"due_date": today}).sort("due_date")
PerformanceSlightly slower because of Object-Document MappingFaster thanks to using Python dictionaries as data models
Usage complexitySimplifies usage by adding an extra level of abstraction and providing an ORM-like experienceRequires a deeper understanding of MondoDB’s internal formats

FAQ

What are Django and MongoDB?

Django and MongoDB are both used in web development but serve different purposes. Django is a high-level Python Web framework that follows the MVT (Model-View-Template) architectural pattern and the DRY (“Don’t Repeat Yourself”) principle.

Is MongoDB good with Django?

Django is optimized for SQL databases. While MongoDB, a NoSQL database, can be integrated through tools like MongoEngine or PyMongo, this can add complexity and limit some native Django features. On the other hand, MongoDB can bring benefits, like flexible data structures and good horizontal scalability. Choosing MongoDB for Django should be based on your specific project needs and readiness to manage potential additional development challenges.

Can you use NoSQL with Django?

Yes, you can use NoSQL databases like MongoDB with Django. However, this can add complexity and might limit some Django features that are optimized for SQL databases. The choice should be based on your specific project requirements and trade-offs.

Which is better, PyMongo or MongoEngine?

PyMongo is faster and offers more direct access to MongoDB, but requires a deeper understanding of MongoDB’s internals. MongoEngine provides a higher-level interface and fits well with Django, but may not support all MongoDB features and can be slower because of abstraction.

Why choose MongoDB over SQL with Django?

MongoDB’s schema flexibility, ability to handle large volumes of data, and unique features like nested objects make it a good fit for certain Django projects. It can scale efficiently across multiple servers. However, using it might limit some of Django’s built-in features that are optimized for SQL databases.

Useful links

Documentation

Tutorials

Videos

Podcasts

Other

image description