Flask + MongoDB Tutorial

5 minute read

In this tutorial I’ll demonstrate how to use Flask together with MongoDB.

Structure

We’ll use the following structure for our project

app/
    __init__.py        # Flask app
    database.py        # Main database class
    models/
        job.py         # Job DB model
    main/
        __init__.py
        routes.py      # Main page routes
    templates
        main.html      # Main page HTML template
requirements.txt       # Requirements for running the application

Flask Mongo Libraries

There are several libraries which provide you with simple integration and convenience helpers when it comes to using Flask together with MongoDB.

Flask-PyMongo Flask-MongoEngine Flask-MongoAlchemy

For this tutorial purpose, we’ll use pymongo as it is, without any helper library.

Create the Flask application

Let’s start by adding the code for creating the application.

vi app/__init__.py

from flask import Flask

from app.database import DB


def create_app(config):
    app = Flask(__name__)
    DB.init()
    register_blueprints(app)
    return app


def register_blueprints(app):

    from app.main import bp as main_bp
    app.register_blueprint(main_bp)

A pretty standard function for creating a Flask application. First, we create an instance of Flask, then we initialize the database (we’ll write the code for it in the next section) and finally, we register the blueprints, which allows users to access our app routes.

If you are not familiar with blueprints, I recommend reading about them here. Personally, I find the concept of blueprints very useful, especially when you build large applications.

Add the database module

In order to connect to our database and run operations on it, we would want to have a generic module and class that we can use anywhere in our application code.

vi app/database.py

import pymongo


class DB(object):

    URI = "mongodb://127.0.0.1:27017"

    @staticmethod
    def init():
        client = pymongo.MongoClient(DB.URI)
        DB.DATABASE = client['sample_app']

    @staticmethod
    def insert(collection, data):
        DB.DATABASE[collection].insert(data)

    @staticmethod
    def find_one(collection, query):
        return DB.DATABASE[collection].find_one(query)

We have two basic functions the class adds – insert and find_one. We will use these functions in order to search for documents in our collections and insert new documents.

Note: if your database is using a different port, change the URI accordingly.

Add a Job Model

Now let’s create a model which represents our job. We will use his model for all the operations related to jobs collection in the database.

vi app/models/job.py

import datetime

from app.database import DB


class Job(object):

    def __init__(self, name):
        self.name = name
        self.created_at = datetime.datetime.utcnow()

    def insert(self):
        if not DB.find_one("jobs", {"name": self.name}):
            DB.insert(collection='jobs', data=self.json())

    def json(self):
        return {
            'name': self.name,
            'created_at': self.created_at
        }

The first thing to note is that we import the DB class we created in the previous section. This is our connection to the database and we’ll use it for inserting and searching for jobs.

Next, our Jobs will have only two fields in the documents:

  • name – self-explanatory

  • created_at – datetime object which represents the date when the job was added to the database The ‘insert’ method is the method responsible for inserting the job object into the database, using the ‘json’ method which returns a JSON representation of our object. Note it will only add the object if there is not already such job/document in the jobs collection.

Add Jobs

Now that we have the ability to add some jobs, let’s modify our app creation function to add them once the user started the application

vi app/__init__.py

from flask import Flask

from app.database import DB
from app.models.job import Job


def create_app(config):
    app = Flask(__name__)
    DB.init()
    register_blueprints(app)
    for job_name in ['job1', 'job2', 'job3']:
        new_job = Job(name=job_name)
        new_job.insert()
    return app


def register_blueprints(app):

    from app.main import bp as main_bp
    app.register_blueprint(main_bp)

As you can see, we are adding three jobs to our database, using the names in the list. The ‘created_at’ attribute has a default value so we don’t need to pass it.

Main Route

Remember we registered a blueprint called ‘main’ in app/init.py? Well, then it’s time to define it. Let’s start by adding app/main/init.py

from flask import Blueprint

bp = Blueprint('main', __name__)

from app.main import routes  # noqa

As you can see we’ll define the routes in its own file in app/main/routes.py

from flask import render_template

from app.main import bp  # noqa
from app.models.job import Job

@bp.route('/')
def index():
    """Main page route."""
    button_text = "Add Job"
    return render_template('main.html', button_text=button_text)


@bp.route('/add_job')
def add_job():
    """Adds job4 to the database."""
    new_job = Job(name='job4')
    new_job.insert()
    return ('', 204)

We defined two routes. First one (‘/’) is for users accessing our app. The only thing this route does is to render the main.html template which we will define in the next section. Every time a user will access our app this way http://x.x.x.x:5000/the main.html will be rendered and displayed to the user in its browser.

The second route (‘/add_job’) will be used to add a job named ‘job4’ when a user clicks on the button we’ll define in the ‘main.html’ template.

Main HTML template

Our main page will be very simple and will include only one simple button with the text “Add Job” on it.

<a id=link><button type="button" class="btn btn-info"></button></a>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type=text/javascript>
        $(function() {
          $('a#link').bind('click', function() {
            $.getJSON('/add_job',
                function(data) {
            });
            return false;
          });
        });
</script>

This template will be rendered the moment the user access our app.

The jquery javascript code is used whenever the user clicks on the button to add a job. It will use the second route we defined for adding the job named ‘job4’.

A question for you to answer: will clicking multiple times on “Add Job” button will add multiple documents of “job4”?

Install requirements

Finally, don’t forget to set up the requirements for running the app successfully

vi requirements.txt

flask
pymongo

Run the application!

That’s it. Perhaps you have the simplest app ever created but it’s a start!

Now all you need to do is to install the requirements and run the app. From the app root directory run the following

virtualenv ~/app_venv && source ~/app_venv/bin/activate
pip install -r requirements.txt
flask run

That’s it. Your app is running! you should see in the terminal similar output to the following

* Environment: production
  WARNING: Do not use the development server in a production environment.
  Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Going to 127.0.0.1:5000 in the browser, you should see a button with “add job” string. Clicking on it, will add

You can verify it with mongo shell the following way

> use sample_app
switched to db sample_app
> db.jobs.find({})
{ "_id" : ObjectId("5c9218017c58a975d123ff8d"), "name" : "job1", "created_date" : ISODate("2019-03-20T10:37:53.353Z") }
{ "_id" : ObjectId("5c9218017c58a975d123ff8e"), "name" : "job2", "created_date" : ISODate("2019-03-20T10:37:53.382Z") }
{ "_id" : ObjectId("5c9218017c58a975d123ff8f"), "name" : "job3", "created_date" : ISODate("2019-03-20T10:37:53.383Z") }
{ "_id" : ObjectId("5c9218067c58a975d123ff90"), "name" : "job4", "created_date" : ISODate("2019-03-20T10:37:58.411Z") }

Few Notes

  • This is one of many possible structures. I usually prefer this one but it doesn’t mean you can’t use a different structure (e.g. skip using blueprints for no good reason :] )

  • A proper project would include several additional files for distributing the project, CLI support, static properties, etc. There are many great projects in GitHub demonstrating this.

  • You can find all the code snippets in this post in my flask-examples repository

Comments