DEV Community

Cover image for Let’s Switch Things Up: Using MongoDB in an Intro Django Project
Anaiya for MongoDB

Posted on

Let’s Switch Things Up: Using MongoDB in an Intro Django Project

This article is written by Anaiya Raisinghani (Developer Advocate @ MongoDB)

Learning how to create your first Django application has never been easier—there is a vast number of resources for developers to follow, and we all know the best way to learn is by example. In this tutorial, we will go through one of the most popular Django tutorials, located on Django’s documentation site, named Writing your first Django app. But this tutorial comes with a twist: Instead of following the tutorial exactly, we will be implementing our django-mongodb-backend library where appropriate.

We will be taking you through the first two parts of this journey, since that is where the implementation will matter most. Once MongoDB is correctly configured as your database, you’ll be able to work with all the Django commands you know and love, without any other configurations.

Before we dive into the tutorial, let’s cover what this library is and why a developer would want to use Django and MongoDB together.

A summary of Django MongoDB Backend

This library, django-mongodb-backend, is a third-party database that integrates seamlessly with Django! Why would a Django developer want to use this library? It aligns with all the familiar steps and commands that Django developers are already accustomed to using in their projects, so besides defining the database backend engine when creating the application, there are no other configuration steps developers need to follow to get up and running.

This process is smooth because MongoDB is committed to supporting as many of Django’s fundamentals as possible. These features include but are not limited to: querying data through the Django QuerySet API, the Django admin panel, representing MongoDB documents through Django models, and more.

Why use a NoSQL database with Django?

There are a variety of reasons why a developer would prefer to use MongoDB in their Django project. MongoDB’s document model, its robust ecosystem of tools, and its inherent competitive advantage set it as a pioneer in the space. While Django and relational databases are the typical pair, using Django with MongoDB will truly accelerate your application.

While Django is known for its simplicity and rapid development, MongoDB completely simplifies data modeling. With these two in tandem—MongoDB as the database back end and Django as the web framework—developers can fully commit to building complex applications instead of starting from square one.

With all this in mind, let’s dive in!

Tutorial pre-reqs

Create your MongoDB cluster

Our first step is to create our MongoDB cluster. We will be using the free tier. Please ensure the “Network Settings” in the cluster are correctly set up. This means making sure your IP address is included in the list of trusted addresses, or setting your cluster access to “Access from Anywhere." Please keep in mind “Access from Anywhere” is not encouraged for secure information, but for the sake of this tutorial, it will be fine. Ensure a secure username and password are set, as well. Once the cluster has been configured, copy and paste the connection string in a safe place as we will use it throughout the tutorial.

Once your cluster has been created, we can start.

Part 1: Basic request and response flow

Before we can get started, we need to create a virtual environment. This is crucial to ensure we can use isolated Python installations for various projects. Go ahead and run the commands below to create your virtual environment:

python3.12 -m venv venv
Enter fullscreen mode Exit fullscreen mode

Then run:

source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Once you see the (venv) next to your directory name in your terminal, you’ll know that you correctly configured your virtual environment. Make sure that your Python version is correct (or 3.10 and above) by double checking with:

python -version
Enter fullscreen mode Exit fullscreen mode

Now, we can install Django 5.0. We can do this and install the MongoDB integration in the same step to make things easier for ourselves when we reach the database connection step.

pip install django-mongodb-backend
Enter fullscreen mode Exit fullscreen mode

This will install both PyMongo and Django since we will need both for the integration. Once the install has completed successfully, we can create a new Django project:

django-admin startproject quickstart --template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/5.0.x.zip
Enter fullscreen mode Exit fullscreen mode

Please make sure the leading version numbers align with the Django version installed—e.g., the link above is for version 5.0.

Structure of new Django project

This will be the structure of your new Django project.

Let’s test the development server to ensure that our Django project works. Make sure you’re in the quickstart root and run this line:

python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

If run correctly, the development server will start:

Correctly ran the development server

When you click on the link, you’ll see this page. Congratulations!

Django congratulations page

The Django development server has been correctly created. It’s a lightweight web server specifically built for rapid development.

Now that we know our environment is correctly configured, we can start building our actual project.

Creating the Polls app

Make sure that you’re in the same directory where you ran the server and run this command:

python manage.py startapp polls --template https://github.com/mongodb-labs/django-mongodb-app/archive/refs/heads/5.0.x.zip
Enter fullscreen mode Exit fullscreen mode

Polls directory

This command created the directory polls. It looks like this.

Now, let’s write our first view! Open up the polls/views.py file and copy in the following code:

from django.http import HttpResponse
from django.shortcuts import render


def index(request):
  return HttpResponse("Hello! You've reached the Django MongoDB sample app landing page.")
Enter fullscreen mode Exit fullscreen mode

This is our very basic view. Now, let’s access it in the browser. To do this, we need to map it to a URL. To define our URL configuration, create a file under the polls application named urls.py. Insert the following:

from django.urls import path


from . import views


urlpatterns = [
   path("", views.index, name="index"),
]
Enter fullscreen mode Exit fullscreen mode

Please ensure your app directory looks like this:

app directory

Now, we need to configure our global URL configuration from our quickstart project to include the URL configuration that we just defined in our polls.urls. So we can do this by adding in an import for django.urls.include in quickstart/urls.py and inserting an include() in the urlspatterns list.

Here is the code to copy into our quickstart/urls.py file:

from django.contrib import admin
from django.urls import include, path


urlpatterns = [
   path("polls/", include("polls.urls")),
   path("admin/", admin.site.urls),
]
Enter fullscreen mode Exit fullscreen mode

In this code snippet, the path() function expects at least two arguments, route and view. The include() function is for referencing the URL configurations.

You have successfully configured an index view into the URL configuration. Let’s ensure it’s working by running the server:

python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Make sure you go to http://localhost:8000/polls/ in your browser and you’ll see the text that we defined from above!

Screenshot of the landing page

Now that we have completed the basic request and response flow, let’s start incorporating our MongoDB database.

Part 2: Database set-up

Open up the quickstart/settings.py file. Head over to the DATABASES setting. Since we created our cluster through MongoDB Atlas, we can use the django_mongodb_backend.parse_uri(MONGODB_URI) function to parse in our connection string.

DATABASES = {
   "default": django_mongodb_backend.parse_uri(mongodb+srv://<username>:<password>@samplecluster.jkiff1s.mongodb.net/<database_name>?retryWrites=true&w=majority&appName=SampleCluster),
}
DATABASES["default"]["NAME"] = "pollsproject"
Enter fullscreen mode Exit fullscreen mode

Make sure to specify the name of your database. This was created above when spinning up our cluster.

Take a look at the INSTALLED_APPS setting above where we just enabled our database configuration. By default, we have these six:

INSTALLED_APPS = [
   'quickstart.apps.MongoAdminConfig',
   'quickstart.apps.MongoAuthConfig',
   'quickstart.apps.MongoContentTypesConfig',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
]
Enter fullscreen mode Exit fullscreen mode

Notice how instead of the traditional Django apps, we have MongoDB apps. django.contrib.admin has been replaced withquickstart.apps.MongoAdminConfig. django.contrib.auth has been replaced with quickstart.apps.MongoAuthConfig, and instead of django.contrib.contenttypes, we have quickstart.apps.MongoContentTypesConfig.

These are MongoDB versions of various Django apps. Each app that is included in our INSTALLED_APPS setting references the corresponding AppConfig.

Now, we need to run our migrate command to specifically create our history. For each migration we create, it will create a database and a collection. Our database name has been specified when we connected to our MongoDB database. We will be able to see a message for each migration that gets applied.

Now, run this command:

python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

When this command is run successfully, you will see this:

(venv) anaiya.raisinghani@M-FQJJWDQ4CK quickstart % python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying sessions.0001_initial... OK
(venv) anaiya.raisinghani@M-FQJJWDQ4CK quickstart % 
Enter fullscreen mode Exit fullscreen mode

Inside our MongoDB cluster with the collections created

Inside of our MongoDB Atlas cluster, we are able to see the collections created for each of the installed Django apps and their models. This includes auth-related collections, like users, groups, permissions, session management, and migrations tracking.

This indicates Django is successfully using MongoDB as the database back end and is creating the necessary collections for our apps!

Creating models

Now that our database has been successfully configured, we can define our models. This is the layout of our database.

Following along in the tutorial for our poll app, we will create a Question model and a Choice model. Our Question model has a question along with a publication date. The Choice model has two fields: the text field and a vote tally. Every single Choice is associated with a Question.

To incorporate these models, we will need to edit our polls/models.py file. Copy in the below code:

from django.db import models




class Question(models.Model):
   question_text = models.CharField(max_length=200)
   pub_date = models.DateTimeField("date published")




class Choice(models.Model):
   question = models.ForeignKey(Question, on_delete=models.CASCADE)
   choice_text = models.CharField(max_length=200)
   votes = models.IntegerField(default=0)
Enter fullscreen mode Exit fullscreen mode

This code here tells Django to create a database schema we can use to access the QuerySet API for Question and Choice objects.

Let’s head over and activate our models so we can install the polls app.

For more information on the fields specified, please check out the Django project tutorial.

Activating our models

To include our app, we need to add it to the INSTALLED_APPS in the settings file. Since the class we are working with is called PollsConfig and it’s in the polls/apps.py file, the path is polls.apps.PollsConfig.

Head over to the quickstart/settings.py file and add this path to the INSTALLED_APPS setting. Once done, it will look like this:

INSTALLED_APPS = [
   'polls.apps.PollsConfig',
   'quickstart.apps.MongoAdminConfig',
   'quickstart.apps.MongoAuthConfig',
   'quickstart.apps.MongoContentTypesConfig',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
]
Enter fullscreen mode Exit fullscreen mode

This lets Django know to install the polls app.

Run this command:

python manage.py makemigrations polls
Enter fullscreen mode Exit fullscreen mode

Your output will look like this:

Output for creating the polls app

makemigrations allows Django to know the models have been created and these changes are going to be stored as a migration.

The way Django stores changes to models is through migrations, which is why they are crucial for applications. Reading the migration for a new model can be done through the sqlmigrate command. Even though we are using MongoDB as our database, with the django-mongodb-backend library, all Django commands are the same, making it easier than ever to get started. Run the command below to see the structure of our migration for our new model under polls/migrations/0001_initial.py:

python manage.py sqlmigrate polls 0001
Enter fullscreen mode Exit fullscreen mode

Something very similar to the screenshot below will be produced through this command.

Using the sqlmigrate command to understand our migration results

The output after this command will be different depending on the database decided on. This is what it will look like when utilizing MongoDB as your back end.

Let’s create our models in our database. Do this by running migrate again:

python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

We get this result:

migrations

Once this command has been run, check the polls/migrations/0001_initial.py file. It will look like this:

# Generated by Django 5.0.11 on 2025-01-21 23:34


import django.db.models.deletion
import django_mongodb_backend.fields
from django.db import migrations, models




class Migration(migrations.Migration):


   initial = True


   dependencies = [
   ]


   operations = [
       migrations.CreateModel(
           name='Question',
           fields=[
               ('id', django_mongodb_backend.fields.ObjectIdAutoField(primary_key=True, serialize=False)),
               ('question_text', models.CharField(max_length=200)),
               ('pub_date', models.DateTimeField(verbose_name='date published')),
           ],
       ),
       migrations.CreateModel(
           name='Choice',
           fields=[
               ('id', django_mongodb_backend.fields.ObjectIdAutoField(primary_key=True, serialize=False)),
               ('choice_text', models.CharField(max_length=200)),
               ('votes', models.IntegerField(default=0)),
               ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
           ],
       ),
   ]

Enter fullscreen mode Exit fullscreen mode

As we can see, instead of Django’s BigAutoField or AutoField for our IDs, we have django_mongodb_backend.fields.ObjectIdAutoField.

Playing with the API

Pull up the Python shell with this command:

python manage.py shell
Enter fullscreen mode Exit fullscreen mode

Import the model classes we just wrote with:

>>> from polls.models import Choice, Question
Enter fullscreen mode Exit fullscreen mode

Take a look and check which questions are in the system:

# No questions are in the system yet.
>>> Question.objects.all()
Enter fullscreen mode Exit fullscreen mode

There are none so this will be the response:

<QuerySet []>
Enter fullscreen mode Exit fullscreen mode

Let’s create a new question.
First, import the timezone and ask a question:

>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
Enter fullscreen mode Exit fullscreen mode

Go ahead and save:

>>> q.save()
Enter fullscreen mode Exit fullscreen mode

Take a look at the ID:

>>> q.id
Enter fullscreen mode Exit fullscreen mode

The ID comes up as:

ObjectId('67903543e362728a32306c98')
Enter fullscreen mode Exit fullscreen mode

Now, let’s take a look at the question we just created:

>>> q.question_text
Enter fullscreen mode Exit fullscreen mode

We will see the question we asked:

"What's new?"
Enter fullscreen mode Exit fullscreen mode

Let’s change the question by changing our attributes. Remember to call save() when done.

>>> q.question_text = "What's up?"
>>> q.save()
Enter fullscreen mode Exit fullscreen mode

Let’s display all the questions we currently have in our database:

>>> Question.objects.all()
<QuerySet [<Question: Question object (67903543e362728a32306c98)>]>
Enter fullscreen mode Exit fullscreen mode

We are able to see the ID, but that isn’t helpful for readability. Close the shell using exit().

Let’s add a string method to both our Question and Choice in our polls/models.py file:

import datetime


from django.db import models
from django.utils import timezone


class Question(models.Model):
   question_text = models.CharField(max_length=200)
   pub_date = models.DateTimeField("date published")


   def was_published_recently(self):
       return self.pub_date >= timezone.now() - datetime.timedelta(days=1)


   def __str__(self):
       return self.question_text






class Choice(models.Model):
   question = models.ForeignKey(Question, on_delete=models.CASCADE)
   choice_text = models.CharField(max_length=200)
   votes = models.IntegerField(default=0)


   def __str__(self):
       return self.choice_text


Enter fullscreen mode Exit fullscreen mode

Let’s save our changes and start a new Python shell. Do this by running:

python manage.py shell
Enter fullscreen mode Exit fullscreen mode

Check and see if our __str__() addition worked and if we are able to see the question asked from above:

>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
Enter fullscreen mode Exit fullscreen mode

We are also able to view questions that start with specific keywords:

>>> filter = Question.objects.filter(question_text__startswith="What")
>>> filter
<QuerySet [<Question: What's up?>]>
Enter fullscreen mode Exit fullscreen mode

We can also access the question through its ID:

>>> q = filter.first()
>>> Question.objects.get(pk=q.id)
<Question: What's up?>
Enter fullscreen mode Exit fullscreen mode

Now, let’s give the question some choices.

We can display choices from our related object set (we do not have any so far):

>>> q.choice_set.all()
<QuerySet []>
Enter fullscreen mode Exit fullscreen mode

Let’s create three choices:

>>> q.choice_set.create(choice_text="Not much", votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text="The sky", votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text="Just hacking again", votes=0)
Enter fullscreen mode Exit fullscreen mode

We are able to have API access to our related question objects:

>>> c.question
<Question: What's up?>
Enter fullscreen mode Exit fullscreen mode

And we are able to allow our question objects to have access to our choice objects:

>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
Enter fullscreen mode Exit fullscreen mode

Now, let’s delete one of the choices we created. We can use delete() to accomplish this:

>>> c = q.choice_set.filter(choice_text__startswith="Just hacking")
>>> c.delete()
(1, {'polls.Choice': 1})
Enter fullscreen mode Exit fullscreen mode

Exit the shell with exit().

Cool! Now, let’s head onto configuring the Django admin page.

Django admin page

To make things easier for developers, Django entirely automates the creation of admin sites for models. Please keep in mind that this admin site is not intended for all users, only site managers.

First, let’s create an admin user:

python manage.py createsuperuser
Enter fullscreen mode Exit fullscreen mode

This will prompt for a Username, an Email address, and a Password. The Password will prompt twice:

Username: admin
Email address: admin@example.com
Password: **********
Password (again): *********
Superuser created successfully.
Enter fullscreen mode Exit fullscreen mode

Once all the information is in, we can start the development server. Run the command below:

python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Access the website: http://127.0.0.1:8000/admin/.
Here, there will be this admin screen:

Django admin screen

Log in using the previously created credentials.

Admin site

Here, we can see our groups and users. These were provided through our 'quickstart.apps.MongoAuthConfig

Now, let’s make our poll app modifiable from this admin page. First, edit the polls/admin.py file to match the code below:

from django.contrib import admin
from .models import Question
admin.site.register(Question)
Enter fullscreen mode Exit fullscreen mode

Once this file is saved, refresh the Django admin site and the Questions will be registered:

Questions have been registered

Now, we are free to edit the questions, if we wish!

Conclusion

In this tutorial, we covered the first two parts of the popular Django Getting Started tutorial. Instead of following the tutorial exactly, we changed up important aspects to integrate the new django-mongodb-backend library so we could use MongoDB as our database. We went over a ton of crucial information:

  • Creating a Django project
  • Starting up the development server
  • Creating an app within a project
  • Creating a view
  • Setting up the database
  • Creating models
  • Activating models
  • Interacting with the API
  • Interacting with Django’s admin interface

For more information on the django-mongodb-backend library, check out our GitHub, and please refer any questions to our Developer Forum!

Top comments (0)