O'Reilly logo

MongoDB and Python by Niall O'Higgins

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. MongoDB with Web Frameworks

While MongoDB can be used in all sorts of applications, its most obvious role is as the database backend for a web application. These days, a great many mobile and tablet applications are functioning as “fat clients” to the same HTTP-based API’s as browser-based web applications; hence mobile and tablet apps need the same sort of backend database infrastructure as more traditional web apps.

Many organizations and engineers are finding the advantages of MongoDB’s document-oriented architecture compelling enough to migrate parts or even entire applications from traditional RDBMS such as MySQL to MongoDB. Numerous well-known companies have built their whole application from the ground up on MongoDB.

It is my opinion that for the vast majority of web, mobile and tablet applications, MongoDB is a better starting point than RDBMS technology such as MySQL. This chapter is an attempt to get you off the ground using MongoDB with three common Python web frameworks: Pylons, Pyramid and Django.

Pylons 1.x and MongoDB

Pylons is one of the older WSGI-based Python web frameworks, dating back to September 2005. Pylons reached version 1.0 in 2010 and is considered very stable at this point. In fact, not much development is planned for Pylons 1.x any more; all new development is happening in Pyramid (see Pyramid and MongoDB for details). The Pylons philosophy is the precise opposite of “one-size-fits-all.” Application developers are free to choose from the various database, templating, session store options available. This kind of framework is excellent when you aren’t exactly sure what pieces you will need when you are starting work on your application. If it turns out you need to use an XML-based templating system, you are free to do so.

The existence of Pyramid aside, Pylons 1.x is a very capable and stable framework. As Pylons is so modular, it is easy to add MongoDB support to it.

First you need to create a virtual environment for your project. These instructions assume you have the virtualenv tool installed on your system. Install instructions for the virtualenv tool are provided in the first chapter of this book.

To create the virtual environment and install Pylons along with its dependencies, run the following commands:

virtualenv --no-site-packages myenv
cd myenv
source bin/activate
easy_install pylons

Now we have Pylons installed in a virtual environment. Create another directory named whatever you like in which to create your Pylons 1.x project, change your working directory to it, then execute:

paster create -t pylons

You will be prompted to enter a name for your project, along with which template engine you want to use and whether or not you want the SQLAlchemy Object-Relational Mapper (ORM). The defaults (“mako” for templating engine, False to SQLAlchemy) are fine for our purposes—not least since we are demonstrating a NoSQL database!

After I ran the paster create command, a “pylonsfoo” directory (I chose “pylonsfoo” as my project name) was created with the following contents:

MANIFEST.in
README.txt
development.ini
docs
ez_setup.py
pylonsfoo
pylonsfoo.egg-info
setup.cfg
setup.py
test.ini

Next you need to add the PyMongo driver as a dependency for your project. Change your working directory to the just-created directory named after your project. Open the setup.py file present in it with your favourite editor. Change the install_requires list to include the string pymongo. Your file should look something like this:

try:
    from setuptools import setup, find_packages
except ImportError:
    from ez_setup import use_setuptools
    use_setuptools()
    from setuptools import setup, find_packages

setup(
    name='pylonsfoo',
    version='0.1',
    description='',
    author='',
    author_email='',
    url='',
    install_requires=[
        "Pylons>=1.0", "pymongo",
    ],
    setup_requires=["PasteScript>=1.6.3"],
    packages=find_packages(exclude=['ez_setup']),
    include_package_data=True,
    test_suite='nose.collector',
    package_data={'pylonsfoo': ['i18n/*/LC_MESSAGES/*.mo']},
    #message_extractors={'pylonsfoo': [
    #        ('**.py', 'python', None),
    #        ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
    #        ('public/**', 'ignore', None)]},
    zip_safe=False,
    paster_plugins=['PasteScript', 'Pylons'],
    entry_points="""
    [paste.app_factory]
    main = pylonsfoo.config.middleware:make_app

    [paste.app_install]
    main = pylons.util:PylonsInstaller
    """,
)

Now you need to fetch the PyMongo driver into your virtual environment. It is easy to do this by executing:

python setup.py develop

Your Pylons app is now ready to be configured with a MongoDB connection. First, we shall create a config file for development

cp development.ini.sample development.ini

Next open the file development.ini in your favourite editor. Underneath the section [app:main] add the following two variables, changing the URI and database names to whatever works for your set up:

mongodb.url = mongodb://localhost
mongodb.db_name = mydb

You can now try starting your project with the following command:

paster serve --reload development.ini

You should see the following output:

Starting subprocess with file monitor
Starting server in PID 82946.
serving on http://127.0.0.1:5000

If you open the URL http://localhost:5000/ in a web browser, you should see the default Pylons page. This means that you have correctly set up your project. However, we do not yet have a way to talk to MongoDB.

Now that the configuration is in place, we can tell Pylons how to connect to MongoDB and where to make the PyMongo connection available to our application. Pylons provides a convenient place for this in <project_name>/lib/app_globals.py. Edit this file and change the contents to the following:

from beaker.cache import CacheManager
from beaker.util import parse_cache_config_options
from pymongo import Connection
from pylons import config

class Globals(object):
    """Globals acts as a container for objects available throughout the
    life of the application

    """

    def __init__(self, config):
        """One instance of Globals is created during application
        initialization and is available during requests via the
        'app_globals' variable

        """
        mongodb_conn = Connection(config['mongodb.url'])
        self.mongodb = mongodb_conn[config['mongodb.db_name']]
        self.cache = CacheManager(**parse_cache_config_options(config))

Once this has been set up, a PyMongo Database instance will be available to your Pylons controller actions through the globals object. To demonstrate, we will create a new controller named “mongodb” with the following command:

paster controller mongodb

You should see a file named mongodb.py in the <project_name>/controllers directory. For demonstration purposes, we shall modify it to increment a counter document in MongoDB every time the controller action is run.

Open this file with your editor. Modify it to look like the following (remembering to change the from pylonsfoo import line into whatever you named your project):

import logging

from pylons import app_globals as g, request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect

from pylonsfoo.lib.base import BaseController, render

log = logging.getLogger(__name__)

class MongodbController(BaseController):

    def index(self):
        new_doc = g.mongodb.counters.find_and_modify({"counter_name":"test_counter"},
            {"$inc":{"counter_value":1}}, new=True, upsert=True , safe=True)
        return "MongoDB Counter Value: %s" % new_doc["counter_value"]

Once you have saved these changes, in a web browser open the URL http://localhost:5000/mongodb/index. Each time you load this page, you should see a document in the counters collection be updated with its counter_value property incremented by 1.

Pyramid and MongoDB

Pyramid is an unopinionated web framework which resulted from the merge of the repoz.bfg framework into the Pylons umbrella project (not to be confused with Pylons 1.x, the web framework). Pyramid can be considered to be a bit like a Pylons 2.0; it is a clean break, a completely new codebase with no code-level backwards compatibility with Pylons 1.x.

However, many of the concepts are very similar to the older Pylons 1.x. Pyramid is where all the new development is happening, and it has fantastic code test coverage and documentation. This section is only intended to be a brief introduction to setting up a Pyramid project with a MongoDB connection. To learn more, refer to the excellent Pyramid book and other resources available free online at http://docs.pylonsproject.org/.

On its own, Pyramid is just a framework, a set of libraries you can use. Projects are most easily started from a what is known as a scaffold. A scaffold is like a project skeleton which sets up plumbing and placeholders for your code.

A number of different scaffolds are included with Pyramid, offering different persistence options, URL mappers and session implementations. Conveniently enough, there is a scaffold called pyramid_mongodb which will build out a skeleton project with MongoDB support for you. pyramid_mongodb eliminates the need for you to worry about writing the glue code to make a MongoDB connection available for request processing in Pyramid.

As with Pylons 1.x, to start using Pyramid you first need to create a virtual environment for your project. These instructions assume you have the virtualenv tool installed on your system. Install instructions for the virtualenv tool are provided in the first chapter of this book.

To create the virtual environment and install Pyramid and its dependencies, run the following commands:

virtualenv --no-site-packages myenv
cd myenv
source bin/activate
easy_install pyramid

Take note of the line sourcing the bin/activate script. It is important to remember to do this once in every shell to make the virtual environment active. Without this step, your default system Python install will be invoked, which does not have Pyramid installed.

Now your virtual environment has Pyramid and all its dependencies installed. However, you still need pyramid_mongodb and its dependencies like PyMongo etc. Run the following command to install pyramid_mongodb in your virtual environment:

easy_install pyramid_mongodb

With Pyamid and pyramid_mongodb installed in your virtual environment, you are ready to create a Pyramid project with MongoDB support. Decide upon a project directory and a project name. From that project directory execute in the shell:

paster create -t pyramid_mongodb <project_name>

After I ran the paster create command, a “mongofoo” directory (I chose “mongofoo” as my project name) was created with the following contents:

README.txt
development.ini
mongofoo
mongofoo.egg-info
production.ini
setup.cfg
setup.py

The default configuration files tell Pyramid to connect to a MongoDB server on localhost, and a database called “mydb”. If you need to change that, simply edit the mongodb.url and mongodb.db_name settings in the INI-files. Note that if you do not have a MongoDB server running at the address configured in the INI-file, your Pyramid project will fail to start.

Before you can run or test your app, you need to execute:

python setup.py develop

This will ensure any additional dependencies are installed. To run your project in debug mode, simply execute:

paster serve --reload development.ini

If all went well, you should see output like the following:

Starting subprocess with file monitor
Starting server in PID 54019.
serving on 0.0.0.0:6543 view at http://127.0.0.1:6543

You can now open http://localhost:6543/ in a web browser and see your Pyramid project, with the default template. If you made it this far, Pyramid is correctly installed and pyramid_mongodb was able to successfully connect to the configured MongoDB server.

The pyramid_mongodb scaffold sets up your Pyramid project in such a way that there is a PyMongo Database object attached to each request object. To demonstrate how to use this, open the file <project_name>/views.py in your favourite editor. There should be a skeletal Python function named my_view:

def my_view(request):
    return {'project':'mongofoo'}

This is a very simple Pyramid view callable. Pyramid view callables are similar to controller actions in Pylons 1.x, and are where much of the application-defined request processing occurs. Since view callables are passed an instance of a request object, which in turn has a property containing the PyMongo Database object, this is an ideal place to interact with MongoDB.

Imagine a somewhat contrived example whereby we wish to insert a document into a collection called “page_hits” each time the my_view view callable is executed. We could do the following:

import datetime
def my_view(request):
    new_page_hit = {"timestamp":datetime.datetime.utcnow(), "url":request.url}
    request.db.page_hits.insert(new_page_hit, safe=True)
    return {"project":"mongofoo"}

If you now reload the web page at http://localhost:6543 you should see a collection called “page_hits” in the MongoDB database you configured in your INI-file. In this collection there should be a single document for each time the view has been called.

From here, you should be well on your way to building web applications with Pyramid and MongoDB.

Django and MongoDB

Django is proabably the most widely-used Python web framework. It has an excellent community and many plugins and extension modules. The Django philosophy is the opposite of Pylons or Pyramid; it offers one well-integrated package including its own database and ORM layer, templating system, URL mapper, admin interface and so on.

There are a number of options for running Django with MongoDB. Since the Django ORM is such an integral part of Django, there is a project known as Django MongoDB Engine which attempts to provide a MongoDB backend for the Django ORM. However, this approach heavily abstracts the underlying query language and data model, along with many of the low-level details discussed in the course of the book. If you are already familiar with the Django ORM, enjoy using it, and are willing to use a fork of Django, Django MongoDB Engine is worth a look. You can find more information at the website http://django-mongodb.org/.

Our recommended approach for now is to use the PyMongo driver directly with Django. Be aware, however, that with this method, the Django components which depend on the Django ORM (admin interface, session store etc) will not work with MongoDB. There is another project called Mango which attempts to provide MongoDB-backed session and authentication support for Django. You can find Mango at https://github.com/vpulim/mango.

10gen have made a sample Django app with PyMongo integration available. This sample app can be found at https://github.com/mdirolf/DjanMon. We shall step through running the sample Django + MongoDB app on your local machine, and examine how it sets up the MongoDB connection.

First, download the sample Django project. If you already have the git command line tools installed, you can run git clone https://github.com/mdirolf/DjanMon.git. Otherwise, simply click the “Download” button at https://github.com/mdirolf/DjanMon.

In order to successfully run the sample app, you will need to build a Python virtual environment with Django, pymongo and PIL installed. As with Pylons and Pyramid, you will first need to have the virtualenv tool installed on your system—details on how to do this are covered in the first chapter of this book. Once you have virtualenv installed, chose a directory in which to store virtual env, then execute the following shell commands in it:

virtualenv --no-site-packages djangoenv
cd djangoenv
source bin/activate
pip install django pymongo PIL

This will create your virtual environment, activate it and then install Django, the PyMongo driver and the PIL image manipulation library (required by the demo app) into it. Assuming this all succeeded, you are ready to start the sample app development server. Note that the sample app expects a MongoDB server to be running on localhost.

Now we can run 10gen’s Django demonstration app. Change your current working directory to your copy of the “DjanMon” project. There should be a file called manage.py in the current working directory. The app can be run with the Django development server with the command:

python manage.py runserver

You should see output on the console like the following:

Validating models...

0 errors found
Django version 1.3, using settings 'DjanMon.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Now you can open a web browser and visit http://localhost:8000/ and see the demonstration app! The app lets you create simple messages (optionally with attached images) which are persisted in MongoDB.

Let us examine how the sample app works. Take a look at the file status/view.py. This is where the MongoDB connection is created, and where most of the application logic is stored. In their Django + MongoDB integration example, 10gen take a different approach from the others outlined in this chapter. They create a PyMongo Database in the global scope of the views module, rather than attaching it to request objects as in Pyramid or making it a framework-wide global as in Pylons 1.x:

import datetime
import string
import random
import mimetypes
import cStringIO as StringIO

from PIL import Image
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from pymongo.connection import Connection
from pymongo import DESCENDING
import gridfs

db = Connection().sms

This approach is simple and works fine for a demo. However, in larger Django projects with multiple installed applications (in this sample, there is a single installed app—it is named “status”) this would require a separate PyMongo connection pool to be maintained for each app. This results in wasted MongoDB connections and duplicated code. Instead, it would be recommended to create the connection in a single place and import it in any other modules which need access.

This should be enough information to get you started building your Django MongoDB application.

Going Further

In this book we have tried to give you a solid grasp of how to leverage MongoDB in real-world applications. You should have a decent understanding of how to go about modeling your data, writing effective queries and avoiding concurrency problems such as race conditions and deadlocks. There are a number of other advanced topics which we didn’t have space for in this book but are nonetheless worth looking into as you build your application. Notably, map-reduce enables computing aggregates efficiently. Sharding permits you to scale your application beyond the available memory of a single machine. GridFS allows you to store binary data in MongoDB. Capped Collections are a special type of collection, which look like a circular buffer and are great for log data. With these features at your disposal, Python and MongoDB are extremely powerful tools to have in your toolbox when developing an application.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required