« Basic Commands to Get You Started with MongoDB

February 21, 2010

By Reza Muhammad

5 Comments, No TrackBacks

Create a Simple App with Django and MongoDB: Part 1

In my previous post, I talked about using MongoDB. Once I felt I started to grasp the idea of it, I wanted to try to use it in a more useful application. This is my first attempt on creating an application on Django using MongoDB. If there is something wrong in what I do, please let me know. I'll be more than happy to receive suggestions.

This is a first part of a (supposedly) multiple series, but I am no expert by any means. I'm writing these down as a way to explore more about MongoDB, and if I can finish this multiple series, I will then start evaluating whether MongoDB is suitable for any of my projects. Today, we are only going scratch the surface by focusing on creating documents and inserting some data to those documents. Hopefully, the other parts of this series will be updated in a more predictable timeframe.

Application Background

As an experiment, I was thinking to create a simple application where I can create tickets with multiple statuses (kind of like an issue tracker, but a simple one). I also want to know if there are any updates to its status over the time.

I learned most of this stuff from Django-Mumblr, and MongoEngine documentation. So thanks to them for making it opensource.

Requirements

In order to make Django work with MongoDB, we need to have a Python driver for MongoDB. It's called pyMongo, and you can download it with pip, or easy_install. Another library we need to have is MongoEngine, an ORM-like (they call it Object-Document-Mapper) library that interacts with MongoDB. MongoEngine itself needs pyMongo, so instead of working with whatever API pyMongo provides, we are going to use MongoEngine API instead. To install MongoEngine, you can run:

$ sudo pip install mongoengine

The above command will install mongoengine, as well as pymongo.

Creating Django Environment

We will start by having a project, and one application. We run:

$ django-admin.py startproject mongoticket
$ cd mongoticket
$ python manage.py startapp ticket

Once the project (mongoticket) and the application (ticket) is created, we need to create a connection to tell Django to access which database. In settings.py under the project directory, we need to add these two lines:

from mongoengine import connect
connect('ticket', 'dummy', 'dummy')

This tells that the project will use a database called "ticket" with a user "dummy" and a password "dummy". If you need to learn more on how to create MongoDB database with authentication, you can see my reference from my other post

Creating a Document Schema

I think I mentioned it before that MongoDB itself is a schema-free database engine. However, I think having schema is still better than not having schema at all. Some rows can have more fields than the others, but for the most part, they should have some identical fields. This is what MongoEngine provides. Besides, I think I'm still used to the concept of RDBMS, so not having a schema at all still frightens me.

Anyway, let's create a file called document.py inside ticket directory, and here's the content of the file:

from mongoengine import *

class Status(Document):
    name = StringField()


class TicketStatus(EmbeddedDocument):
    before = ReferenceField(Status)
    after = ReferenceField(Status)
    changed_on = DateTimeField()


class Ticket(Document):
    description = StringField(required=True, unique=True)
    created_on = DateTimeField()
    changes = ListField(EmbeddedDocumentField(TicketStatus))
  1. Status Document

    Status will be a holder to define statuses. They will only have a few entries such as 'New', 'Accepted', 'On Progress', 'Closed', 'Rejected' and so on. If we were using a Django ORM, we could've done it with Choices, but I am not sure if MongoEngine supports that, so we use document instead.

  2. Ticket Document

    Another document is called Ticket. As you can see here, the fields for Ticket is description, created_on and changes. In changes field, I want to have a list of dictionary that tells me when have the statuses been updated.

    For example, if I create a ticket called "fix this bug", I want to know when this ticket was created, when was it accepted, on progress, and finished. In the end, I want to know how long does it take to finish a bug.

    In ORM, this is similar to ManyToOne relationships. In MongoDB, we don't have to as it is a non-relational database engine.

  3. TicketStatus Document

    The third document we have is TicketStatus but it inherits from EmbeddedDocument rather than Document. With EmbeddedDocument, we can embed a particular document to another document. In this case, a changes field in Ticket document will have a list of multiple TicketStatus document. This is similar to Ticket.objects.ticketstatus_set.all() in Django ORM but the difference is TicketStatus collection is never written to the database.

    before and after field is similar to ForeignKey in ORM. It references from Status Document.

Now we are ready to start inserting some data to the database

Inserting Data to the Database

Since we are only going to inserting data in this part, we are not going to create views yet. Rather, we will work on Python shell. We will probably start working on views functions on the next series.

Now, let's create entries to our Status document. Inside your Django project directory (mongoticket), run:

$ python manage.py shell
... snip ...
>>> from ticket.document import Status, TicketStatus, Ticket
>>> from datetime import datetime
>>>
>>> Status(name='New').save()
>>> Status(name='On Progress').save()
>>> Status(name='Rejected').save()
>>> Status(name='Closed').save()
>>> 
>>> new = Status.objects(name='New')[0]
>>> progress = Status.objects(name='On Progress')[0]
>>> rejected = Status.objects(name='Rejected')[0]
>>> closed = Status.objects(name='Closed')[0]

These are the basic statuses we are going to have. In reality, we probably need more, but we will just start with the basics.

Next, let's create a ticket:

>>> new_ticket = Ticket(description='Creating a Document', created_on=datetime.now())
>>> new_ticket.save()

You can now access its fields from new_ticket.description and new_ticket.created_on

Once we have our ticket, let's try to add some changes to it. For example, assuming that the first created ticket has a status of 'New', now let's move it to 'On Progress' as if a ticket has been accepted and someone is working on it.

>>> new_to_progress = TicketStatus(before=new, after=progress, 
...     changed_on=datetime.now())
>>> q = Ticket.objects(id=new_ticket.id)
>>> q.update(push__changes=new_to_progress)

If you notice earlier, changes field in Ticket document contains a list of embedded documents of TicketStatus document. So what we did was, we created a TicketStatus object, we look for the ticket we want to add the its changes to, and we push the object to the changes list.

Let's say that moments later the ticket has been taken care of, and it is now done. We want to add another TicketStatus to be embedded in Ticket document to identify this. For that, we do:

>>> close_ticket = TicketStatus(before=progress, after=closed, 
...     changed_on=datetime.now())
>>> q.update(push__changes=close_ticket)

So now, we should have a ticket called 'Create a document' where it has been created, moved to 'On Progress', and finally 'Closed'. Let's make sure if it works correctly.

>>> q = Ticket.objects(id=new_ticket.id)[0]
>>> for i in q.changes:
...     'From \'%s\' => \'%s\', on %s' % (i.before.name, i.after.name, 
...     i.changed_on.strftime("%B %d, %Y at %I:%M %p"))
... 
u"From 'New' => 'On Progress', on February 21, 2010 at 08:11 AM"
u"From 'On Progress' => 'Closed', on February 21, 2010 at 08:23 AM"
>>>

There you go, it is exactly what we wanted.

That is it for this series. We have created a document schema in document.py, created some objects for each documents and we link them as well as embedding documents to another document. Hopefully, this is helpful for you who are just starting to work with MongoDB on Django. Stay tune for the next series as we will start creating views functions and accessing it from the web.

If you have any comments or suggestions, please let me know.

TrackBacks

TrackBack URL: http://rezmuh.sixceedinc.com/mt/mt-tb.cgi/16

5 Comments

Hi,
nice first-part tutorial.
I wanted to add that we have someone working on a MongoDB backend for Django-nonrel and first features are starting to work. So soon you can use native Django including the model layer and the admin interface with MongoDB!
For more information see
http://www.allbuttonspressed.com/

Again, really clean tutorial.

Bye,
Thomas

Great post - I'm looking forward to the others in the series.

The link to the MongoDB backend is:
http://github.com/ixc/mongodj

I think that a first version will be available in one month, though basic operation work already.
We do not provide a MongoDB documentation but it will work like Django's native ORM with MongoDB limitations like no JOIN-support. We will post on limitations for non-relational databases and how to deal with them on
http://www.allbuttonspressed.com/blog/django
Yesterday we published our first post on this topic.

Let us know if you are interested in helping.

Bye,
Thomas

Leave a comment

Scroll to Top