PDF

Using MongoDB with Django
Adding a document-oriented database to the mix
Cesar Otero
Consultant
Freelance Consultant
21 February 2012
Django, a Python web framework, consists of an object-relational mapper (ORM), back-end
controller, and template system. MongoDB is a document-oriented database (also known as a
NoSQL database) effective for scaling and high performance. In this article, learn how to call
MongoDB from Python (using MongoEngine), and integrate it into a Django project in lieu of
the built-in ORM. A sample web interface for creating, reading, writing, and updating data to the
MongoDB back end is included.
Django is used in such a wonderfully modular style; it's simple to replace different components of
a Django-based web application. Because NoSQL databases are more common these days, you
might want to try running an application with a different back end rather than one of the standard
relational databases such as MySQL®. In this article, you get a light taste of MongoDB, including
how to call it in your Python projects using either PyMongo or MongoEngine. Then you use Django
and MongoEngine to create a simple a blog that can perform Create, Read, Update, and Delete
(CRUD) operations.
About NoSQL databases
According to nosql-database.org, NoSQL databases are "next generation databases mostly
addressing some of the points: being non-relational, distributed, open source, and horizontally
scalable." In this class of database is MongoDB, a document-oriented database.
Go NoSQL in the Cloud with Cloudant
Cloudant is a NoSQL DBaaS is built to scale, run non-stop, and handle a wide variety of data
types like JSON, full-text, and geo spatial.
Out-of-the-box, Django 1.3 includes support for SQLite, MySQL, PostgreSQL, and Oracle
but doesn't include support for MongoDB. However, it's easy to add support for MongoDB.
Unfortunately, the drawback is that you lose the automatic admin panel. Therefore, you have to
weigh this against your needs.
© Copyright IBM Corporation 2012
Using MongoDB with Django
Trademarks
Page 1 of 13
developerWorks®
ibm.com/developerWorks/
Brief introduction to MongoDB
MongoDB acts as a JavaScript interpreter, and hence database manipulation is done through
JavaScript commands. After you install it locally on your machine (see Resources), try some of the
commands shown in Listing 1.
Listing 1. Sample JavaScript commands you can try with MongoDB
var x = "0";
x === 0;
typeof({});
You don't have to be a JavaScript expert to get started with MongoDB; yet, here are a few useful
concepts:
• You can create objects using object literal syntax, in other words with two braces (for
example, var myCollection = {};).
• You can create arrays with brackets ([]).
• Everything in JavaScript is an object except for numbers, Boolean, null, and undefined.
If you want to learn more about JavaScript's other features such as prototypal object-oriented
programming (OOP), scoping rules, and its functional programming nature, see Resources.
MongoDB is schemaless, in stark contrast to relational databases. Instead of tables, you use
collections, which consist of documents. Documents are created using object literal syntax, as
shown in Listing 2.
Listing 2. Document creation examples
var person1 = {name:"John Doe", age:25};
var person2 = {name:"Jane Doe", age:26, dept: 115};
Now, execute the commands shown in Listing 3 to create a new collection.
Listing 3. Creating collections
db.employees.save(person1);
db.employees.save(person2);
Because MongoDB is schemaless, person1 and person2 don't have to have the same column
types or even the same number of columns. Also, MongoDB is dynamic in nature, so it creates
employees rather than throwing an error. You can retrieve documents through the find() method.
To get all of the documents in employees, call find() without any arguments, as shown in Listing
4.
Listing 4. A simple MongoDB query
> db.employees.find();
// returns
[
{
"_id" : {
"$oid" : "4e363c4dcc93747e68055fa1"
},
"name" : "John Doe",
"age" : 25
},
{
"_id" : {
"$oid" : "4e363c53cc93747e68055fa2"
},
"name" : "Jane Doe",
"dept" : 115,
"age" : 26
]
Using MongoDB with Django
}
Page 2 of 13
ibm.com/developerWorks/
developerWorks®
Note that _id is the equivalent of a primary key. To run specific queries, you need to pass another
object with the key/value pair indicating what you're querying for, as shown in Listing 5.
Listing 5. Query by one search parameter
> db.employees.find({name: "John Doe"});
// returns
[
{
"_id" : {
"$oid" : "4e363c4dcc93747e68055fa1"
"name" : "John Doe",
"age" : 25
}
]
},
To query for employees with an age greater than 25, execute the command shown in Listing 6.
Listing 6. Query for employees with an age greater than 25
> db.employees.find({age:{'$gt':25}});
// returns
[
{
"_id" : {
"$oid" : "4e363c53cc93747e68055fa2"
},
"name" : "Jane Doe",
"dept" : 115,
"age" : 26
}
]
The $gt is a special operator that means greater than. Table 1 lists some other modifiers.
Table 1. Modifiers you can use with MongoDB
Modifier
Description
$gt
Greater than
$lt
Less than
$gte
Greater than or equals
$lte
Less than or equals
$in
Check for existence in an array, similar to the 'in' SQL operator.
You can, of course, update a record by using the update() method. You can update the entire
record, as shown in Listing 7.
Listing 7. Update an entire record
> db.employees.update({
name:"John Doe", // Document to update
{name:"John Doe", age:27} // updated document
});
Alternatively, you can update just a single value using the $set operator, as shown in Listing 8.
Listing 8. Update a single value in a record
> db.employees.update({name:"John Doe",
{ '$set': {age:27} }
});
Using MongoDB with Django
Page 3 of 13
developerWorks®
ibm.com/developerWorks/
To empty a collection, call the remove() method without any arguments. For instance, if you want
to remove John Doe from the employees collection, you could do what's shown in Listing 9.
Listing 9. Remove John Doe from the employees collection
> db.employees.remove({"name":"John Doe"});
> db.employees.find();
// returns
[
{
"_id" : {
"$oid" : "4e363c53cc93747e68055fa2"
"dept" : 115,
"age" : 26
}
]
},
"name" : "Jane Doe",
That's just enough to get you started. Of course you can continue to explore on the official website,
which has a neat web-based interactive mongodb command prompt complete with tutorial as well
as the official documents. See Resources.
Integrating Django with MongoDB
You have a few options for accessing MongoDB from Python or Django. The first is using the
Python module, PyMongo. Listing 10 is a sample PyMongo session, assuming you've installed
MongoDB and already have an instance running on a port.
Listing 10. Sample PyMongo session
from pymongo import Connection
databaseName = "sample_database"
connection = Connection()
db = connection[databaseName]
employees = db['employees']
person1 = { "name" : "John Doe",
"age" : 25, "dept": 101, "languages":["English","German","Japanese"] }
person2 = { "name" : "Jane Doe",
"age" : 27, "languages":["English","Spanish","French"] }
print "clearing"
employees.remove()
print "saving"
employees.save(person1)
employees.save(person2)
print "searching"
for e in employees.find():
print e["name"] + " " + unicode(e["languages"])
PyMongo allows you to run more than one database concurrently. To define a connection, simply
pass in a database name to a connection instance. Python dictionaries, in this case, substitute
the JavaScript object literals for creating new document definitions, and Python lists substitute
JavaScript arrays. The find method returns a database cursor object that you can iterate over.
The similarity in syntax makes it easy to switch between the MongoDB command line and running
commands with PyMongo. For example, Listing 11 shows how to run a query with PyMongo.
Using MongoDB with Django
Page 4 of 13
ibm.com/developerWorks/
developerWorks®
Listing 11. Run a query with PyMongo
for e in employees.find({"name":"John Doe"}):
print e
Your other option for calling MongoDB from Python is MongoEngine, which should feel familiar if
you've used Django's built-in ORM. MongoEngine is a document-to-object mapper, which is similar
in concept to an ORM. Listing 12 shows an example session with MongoEngine.
Listing 12. Example MongoEngine session
from mongoengine import *
connect('employeeDB')
class Employee(Document):
name = StringField(max_length=50)
age = IntField(required=False)
john = Employee(name="John Doe", age=25)
john.save()
jane = Employee(name="Jane Doe", age=27)
jane.save()
for e in Employee.objects.all():
print e["id"], e["name"], e["age"]
The Employee object inherits from mongoengine.Document. In this example, you use two field
types: StringField and IntField. Similar to Django's ORM, to query for all the documents in the
collection you call Employee.objects.all(). Notice that to access the unique object ID, you use
"id" rather than "_id".
A sample blog
Now you'll create a simple blog called Blongo. You'll use Python 2.7, Django 1.3, MongoDB 1.8.2,
MongoEngine 0.4, and Hypertext Markup Language (HTML) 5. If you want to recreate my exact
settings, I used Ubuntu Linux with FireFox. Blongo displays any blog entries entered upon page
load and allows updating and deleting of any entries—in other words, all the standard CRUD
operations. The Django views have three methods: index, update, and delete.
The cascading style sheets (CSS) definitions go in a separate static file. I won't go into details
here, but feel free to explore the source code included in Download.
Assuming everything is installed and running well, create a new Django project and the necessary
components as shown in Listing 13.
Listing 13. Commands for setting up the Django blog project
$
$
$
$
$
$
django-admin.py startproject blongo
cd blongo
django-admin.py startapp blogapp
mkdir templates
cd blogapp
mkdir static
Using MongoDB with Django
Page 5 of 13
developerWorks®
ibm.com/developerWorks/
New to Django 1.3 is an included contributed application for improved handling of static files. By
adding a static directory to any application directory (such as blogapp in this case) and making
sure that django.contrib.staticfiles is included in the installed applications, Django is able to find
static files such as .css and .js files without needing any additional tweaks. Listing 14 shows the
lines of the settings files that have been altered (from the default settings.py file) to get the blog
application running.
Listing 14. Lines of the settings files that have been altered (from the default
settings.py file)
# Django settings for blog project.
import os
APP_DIR = os.path.dirname( globals()['__file__'] )
DBNAME = 'blog'
TEMPLATE_DIRS = (
os.path.join( APP_DIR, 'templates' )
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.blogapp',
)
You have three templates in this project as well: index.html, update.html, and delete.html. Listing
15 shows the code for all three template files.
Listing 15. Code for the index.html, update.html, and delete.html template files
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<link href="{{STATIC_URL}}blog.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Blongo</h1>
<form method="post" action="http://127.0.0.1:8000/">
{% csrf_token %}
<ul>
<li>
<input type="text" name="title" placeholder="Post Title" required>
</li>
<li>
<textarea name="content" placeholder="Enter Content" rows=5 cols=50 required>
</textarea>
</li>
<li>
<input type="submit" value="Add Post">
</li>
</ul>
</form>
<!-- Cycle through entries -->
{% for post in Posts %}
<h2> {{ post.title }} </h2>
<p>{{ post.last_update }}</p>
Using MongoDB with Django
Page 6 of 13
ibm.com/developerWorks/
developerWorks®
<p>{{ post.content }}</p>
<form method="get" action="http://127.0.0.1:8000/update">
<input type="hidden" name="id" value="{{ post.id }}">
<input type="hidden" name="title" value="{{ post.title }}">
<input type="hidden" name="last_update" value="{{ post.last_update }}">
<input type="hidden" name="content" value="{{ post.content }}">
<input type="submit" name="" value="update">
</form>
<form method="get" action="http://127.0.0.1:8000/delete">
<input type="hidden" name="id" value="{{post.id}}">
<input type="submit" value="delete">
</form>
{% endfor %}
</body>
</html>
<!-- update.html -->
<!DOCTYPE html>
<html>
<head>
<link href="{{STATIC_URL}}blog.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Blongo - Update Entry</h1>
<form method="post" action="http://127.0.0.1:8000/update/">
{% csrf_token %}
<ul>
<li><input type="hidden" name="id" value="{{post.id}}"></li>
<li>
<input type="text" name="title" placeholder="Post Title"
value="{{post.title}}" required>
<input type="text" name="last_update"
value="{{post.last_update}}" required>
</li>
<li>
<textarea name="content" placeholder="Enter Content"
rows=5 cols=50 required>
{{post.content}}
</textarea>
</li>
<li>
<input type="submit" value="Save Changes">
</li>
</ul>
</form>
</body>
</html>
<!-- delete.html -->
<!DOCTYPE html>
<html>
<head>
<link href="{{STATIC_URL}}blog.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Blongo - Delete Entry</h1>
<form method="post" action="http://127.0.0.1:8000/delete/">
{% csrf_token %}
<input type="hidden" name="id" value="{{id}}">
<p>Are you sure you want to delete this post?</p>
<input type="submit" value="Delete">
</form>
</body>
</html>
Next, change your URL mappings to the code shown in Listing 16, which points to the views for
index, update, and delete. You want the sample blog to create new blog entries (at the index),
Using MongoDB with Django
Page 7 of 13
developerWorks®
ibm.com/developerWorks/
update existing blog posts, and delete them when desired. Each action is accomplished by posting
to a specific URL.
Listing 16. URL mappings for index, update, and delete
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('',
url(r'^$', 'blog.blogapp.views.index'),
url(r'^update/', 'blog.blogapp.views.update'),
url(r'^delete/', 'blog.blogapp.views.delete'),
)
Notice that you don't need to run the syncdb Django command. To integrate MongoDB into your
application, you need MongoEngine. In the models.py file of the blogapp directory, add the code
shown in Listing 17.
Listing 17. Including MongoEngine in the data layer
from mongoengine import *
from blog.settings import DBNAME
connect(DBNAME)
class Post(Document):
title = StringField(max_length=120, required=True)
content = StringField(max_length=500, required=True)
last_update = DateTimeField(required=True)
The database name is taken from the settings file to separate the concerns. Each blog post
contains three required fields: title, content, and last_update. If you compare and contrast this
listing with what you would normally do in Django, the difference isn't enormous. Instead of having
a class that inherits from django.db.models.Model, this listing uses the mongoengine.Document
class instead. I don't have space here to enter into the difference between the data types, but feel
free to check out the MongoEngine documents (see Resources).
Table 2 lists the MongoEngine field types and shows the equivalent Django ORM field type, if one
exists.
Table 2. MongoEngine field types and Django ORM equivalents
MongoEngine field type
Django ORM equivalent
StringField
CharField
URLField
URLField
EmailField
EmailField
IntField
IntegerField
FloatField
FloatField
DecimalField
DecimalField
BooleanField
BooleanField
DateTimeField
DateTimeField
Using MongoDB with Django
Page 8 of 13
ibm.com/developerWorks/
developerWorks®
EmbeddedDocumentField
None
DictField
None
ListField
None
SortedListField
None
BinaryField
None
ObjectIdField
None
FileField
FileField
Finally, you can set up your views. Here you have three view methods: index, update, and delete.
To execute the intended action, a post request must be made to the specific URL. For example,
to update a document a post must be made to localhost:8000/update. Executing an http 'GET'
request will not save, update, and so on. New blog posts are inserted from the index view. Listing
18 shows the implementations for the index, update, and delete views.
Listing 18. The Django views
from django.shortcuts import render_to_response
from django.template import RequestContext
from models import Post
import datetime
def index(request):
if request.method == 'POST':
# save new post
title = request.POST['title']
content = request.POST['content']
post = Post(title=title)
post.last_update = datetime.datetime.now()
post.content = content
post.save()
# Get all posts from DB
posts = Post.objects
return render_to_response('index.html', {'Posts': posts},
context_instance=RequestContext(request))
def update(request):
id = eval("request." + request.method + "['id']")
post = Post.objects(id=id)[0]
if request.method == 'POST':
# update field values and save to mongo
post.title = request.POST['title']
post.last_update = datetime.datetime.now()
post.content = request.POST['content']
post.save()
template = 'index.html'
params = {'Posts': Post.objects}
elif request.method == 'GET':
template = 'update.html'
params = {'post':post}
return render_to_response(template, params, context_instance=RequestContext(request))
def delete(request):
Using MongoDB with Django
Page 9 of 13
developerWorks®
ibm.com/developerWorks/
id = eval("request." + request.method + "['id']")
if request.method == 'POST':
post = Post.objects(id=id)[0]
post.delete()
template = 'index.html'
params = {'Posts': Post.objects}
elif request.method == 'GET':
template = 'delete.html'
params = { 'id': id }
return render_to_response(template, params, context_instance=RequestContext(request))
You may have noticed the eval statements used to retrieve the document IDs. This is used to
avoid having to write the if statement shown in Listing 19.
Listing 19. Alternate way of retrieving the document ID
if request.method == 'POST':
id = request.POST['id']
elif request.method == 'GET':
id = request.GET['id']
You could also write it that way. That's all it takes to get a simple blog up and running. There are
obviously many components missing for a final product such as users, a login, tags, and so on.
Conclusion
As you can see, there really isn't much to calling MongoDB from Django. In this article, I introduced
MongoDB briefly, and explained how to access it and manipulate its collections and documents
from Python through the PyMongo wrapper and the MongoEngine object-to-document mapper.
Finally, I offered a quick demonstration of how to create a basic CRUD form using Django.
Although this is just a first step, hopefully you now understand how apply this setup in your own
projects.
Using MongoDB with Django
Page 10 of 13
ibm.com/developerWorks/
developerWorks®
Downloads
Description
Name
Size
Sample Django application with MongoEngine
blongo.zip
12KB
Using MongoDB with Django
Page 11 of 13
developerWorks®
ibm.com/developerWorks/
Resources
Learn
• Develop and deploy your next app on the IBM Bluemix cloud platform.
• Learn more about JavaScript from Mozilla's tutorial, "A re-introduction to JavaScript," and
Douglas Crockford's book, JavaScript: The Good Parts (O'Reilly Media/Yahoo Press, May
2008).
• In the developerWorks Open source zone, find extensive how-to information, tools, and
project updates to help you develop with open source technologies and use them with IBM's
products.
• Stay current with developerWorks technical events and webcasts focused on a variety of IBM
products and IT industry topics.
• Attend a free developerWorks Live! briefing to get up-to-speed quickly on IBM products and
tools as well as IT industry trends.
• Follow developerWorks on Twitter.
• Watch developerWorks demos ranging from product installation and setup demos for
beginners, to advanced functionality for experienced developers.
Get products and technologies
•
•
•
•
•
•
Learn more about and download MongoDB.
Download and explore Django.
Visit the Python website for downloads and documentation.
Check out MongoEngine.
Dig in to PyMongo.
Evaluate IBM products in the way that suits you best: Download a product trial, try a product
online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox
learning how to implement Service Oriented Architecture efficiently.
Discuss
• Get involved in the developerWorks community. Connect with other developerWorks users
while exploring the developer-driven blogs, forums, groups, and wikis.
Using MongoDB with Django
Page 12 of 13
ibm.com/developerWorks/
developerWorks®
About the author
Cesar Otero
Cesar Otero is a freelance Java and Python consultant. He holds a degree in
electrical engineering with a minor in mathematics.
© Copyright IBM Corporation 2012
(www.ibm.com/legal/copytrade.shtml)
Trademarks
(www.ibm.com/developerworks/ibm/trademarks/)
Using MongoDB with Django
Page 13 of 13