Usando o MongoDB com Django

Incluindo um banco de dados orientado a documento à combinação

O Django, uma estrutura da web do Python, consiste em um sistema de object-relational mapper (ORM), controlador de backend e modelo. O MongoDB é um banco de dados orientado a documento (também conhecido como um banco de dados NoSQL) eficaz para ajuste de escala e alto desempenho. Neste artigo, saiba como chamar o MongoDB a partir do Python (usando MongoEngine) e integrá-lo a um projeto Django no lugar do ORM integrado. Uma interface da web de amostra é incluída para criar, ler, escrever e atualizar dados para o backend do MongoDB.

Cesar Otero, Consultant, Freelance Consultant

Cesar OteroCesar Otero é consultor Java e Python freelance. Ele é formado em engenharia elétrica com especialização em matemática.



08/Mar/2012

O Django é usado em um estilo maravilhosamente modular; é simples para substituir os diferentes componentes de um aplicativo Django baseado na web. Como os bancos de dados NoSQL são mais comuns nos dias de hoje, pode ser que você queira tentar executar um aplicativo com um backend diferente, em vez de um dos bancos de dados relacionais padrão, como o MySQL®. Neste artigo, você começa a saborear o MongoDB, incluindo como chamá-lo em seus projetos Python usando o PyMongo ou o MongoEngine. Logo, você usa o Django e o MongoEngine para criar um blog simples que pode executar operações Create, Read, Update, and Delete (CRUD).

Sobre os bancos de dados NoSQL

De acordo com o nosql-database.org, os bancos de dados NoSQL são a "próxima geração de bancos de dados endereçando primordialmente alguns dos pontos: ser não relacional, distribuído, software livre e escalável horizontalmente". Nesta classe de banco de dados está o MongoDB, um banco de dados orientado a documento.

Na prática do dia a dia, o Django 1.3 inclui suporte ao SQLite, MySQL, PostgreSQL e Oracle, mas não inclui suporte ao MongoDB. No entanto, é fácil incluir suporte ao MongoDB. Infelizmente, a desvantagem é que você perde o painel de administração automática. Portanto, você tem que pesar isso com relação às suas necessidades.


Breve introdução ao MongoDB

O MongoDB age como um interpretador de JavaScript e, portanto, a manipulação do banco de dados é feita por meio de comandos JavaScript. Depois de instalá-lo localmente na sua máquina (consulte Recursos), tente alguns dos comandos mostrados em Listagem 1.

Lista 1. Comandos JavaScript de amostra que podem ser testados com o MongoDB
var x = "0";
x === 0;
typeof({});

Não é necessário ser um especialista em JavaScript para começar a usar o MongoDB, ainda assim, seguem alguns conceitos úteis:

  • É possível criar objetos usando a sintaxe literal de objeto, em outras palavras, com duas chaves (por exemplo var myCollection = {};).
  • É possível criar matrizes com colchetes ([]).
  • Tudo no JavaScript é um objeto, exceto para números, variáveis booleanas, nulo e indefinido.

Se você quiser saber mais sobre outros recursos do JavaScript, como a programação orientada a objetos de protótipo (OOP), regras de escopo e a sua natureza de programação funcional, consulte Recursos.

O MongoDB não possui esquemas, contrastando com os bancos de dados relacionais. Em vez de tabelas, coleções, que consistem em documentos, são usadas. Os documentos são criados usando sintaxe literal de objeto, conforme mostrado na Listagem 2.

Lista 2. Exemplos de criação de documento
var person1 = {name:"John Doe", age:25};
var person2 = {name:"Jane Doe", age:26, dept: 115};

Agora, execute os comandos mostrados na Listagem 3 para criar uma nova coleção.

Lista 3. Criando coleções
db.employees.save(person1);
db.employees.save(person2);

Como o MongoDB não possui esquemas, person1 e person2 não precisam ter os mesmos tipos de coluna, ou até o mesmo número de colunas. Além disso, o MongoDB é dinâmico por natureza, por isso cria funcionários ao invés de lançar um erro. É possível recuperar documentos por meio do método find() . Para obter todos os documentos em funcionários, chame find() sem qualquer argumento, conforme mostrado na Listagem 4.

Lista 4. Uma consulta MongoDB simples
> db.employees.find();
// returns
[ 
  {   "_id" : {   "$oid" : "4e363c4dcc93747e68055fa1"   },   
        "name" : "John Doe",   "age" : 25   },
  {   "_id" : {   "$oid" : "4e363c53cc93747e68055fa2"   },   
        "name" : "Jane Doe",   "dept" : 115,   "age" : 26   }
]

Observe que _id é o equivalente a uma chave primária. Para executar consultas específicas, é necessário passar outro objeto com o par de chave/valor que indica o que você está consultando, conforme mostrado na Listagem 5.

Lista 5. Consulta por um parâmetro de pesquisa
> db.employees.find({name: "John Doe"});
// returns
[ 
  {   "_id" : {   "$oid" : "4e363c4dcc93747e68055fa1"   },   
  "name" : "John Doe",   "age" : 25   }
]

Para consultar os funcionários com idade acima de 25 anos, execute o comando mostrado na Listagem 6.

Lista 6. Consulta pelos funcionários com idade superior a 25 anos
> db.employees.find({age:{'$gt':25}});
// returns
[ 
  {   "_id" : {   "$oid" : "4e363c53cc93747e68055fa2"   },   
  "name" : "Jane Doe",   "dept" : 115,   "age" : 26   }
]

O $gt é um operador especial que significa maior do que. A Tabela 1 lista alguns outros modificadores.

Tablela 1. Os modificadores que podem ser usados com o MongoDB
ModificadorDescrição
$gtMaior que
$ltMenor que
$gteMaior ou igual a
$lteMenor ou igual a
$inVerificar a existência de um array, semelhante ao operador 'in' de SQL.

É possível, evidentemente, atualizar um registro usando o método update() . É possível atualizar todo o registro, conforme mostrado na Listagem 7.

Lista 7. Atualizar um registro todo
> db.employees.update({
    name:"John Doe",  // Document to update
    {name:"John Doe", age:27} // updated document
  });

Como alternativa, é possível atualizar apenas um único valor usando o operador $set , conforme mostrado na Listagem 8 .

Lista 8. Atualizar um valor único em um registro
> db.employees.update({name:"John Doe", 
     { '$set': {age:27} }
  });

Para esvaziar uma coleção, chame o método remove() sem qualquer argumento. Por exemplo, se deseja remover o "John Doe" da coleção funcionários, você poderia fazer o que é mostrado na Listagem 9.

Lista 9. Remover o "John Doe" da coleção de funcionários
> db.employees.remove({"name":"John Doe"});
> db.employees.find();
// returns
[ 
  {   "_id" : {   "$oid" : "4e363c53cc93747e68055fa2"   },   "name" : "Jane Doe",   
      "dept" : 115,   "age" : 26   }
]

Isso é apenas o suficiente para você começar. Claro que você pode continuar explorando o site oficial, que possui um prompt de comando mongodb interativo e puro baseado na web completo com tutorial, bem como os documentos oficiais. Consulte Recursos.


Integrando o Django ao MongoDB

Você tem algumas opções de acesso ao MongoDB a partir do Python ou Django. A primeira é usar o módulo Python, PyMongo. Listagem 10 é uma sessão PyMongo de amostra, supondo que você tenha instalado o MongoDB e já tenha uma instância rodando em uma porta.

Lista 10. Sessão de PyMongo de amostra
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"])

O PyMongo permite que você execute mais de um banco de dados simultaneamente. Para definir uma conexão, basta passar em um nome de banco de dados para uma instância de conexão. Os dicionários Python, neste caso, substituem os literais de objeto JavaScript para criar novas definições de documentos, e as listas de Python substituem matrizes de JavaScript. O método find retorna um objeto cursor de banco de dados sobre o qual é possível iterar.

A semelhança na sintaxe facilita a alternância entre a linha de comando MongoDB e a execução de comandos com PyMongo. Por exemplo, a Listagem 11 mostra como executar uma consulta com o PyMongo.

Lista 11. Executar uma consulta com o PyMongo
for e in employees.find({"name":"John Doe"}):
    print e

A sua outra opção para chamar o MongoDB a partir do Python é o MongoEngine, que deve ser familiar caso você já tenha usado o ORM integrado do Django. O MongoEngine é um mapeador de documento para objeto, que é semelhante, em conceito, a um ORM. Listagem 12 mostra uma sessão de exemplo com o MongoEngine.

Lista 12. Sessão de exemplo do MongoEngine
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"]

O objeto Employee recebe herança de mongoengine.Document. Neste exemplo, dois tipos de campo são usados: StringField e IntField. Assim como o ORM do Django, para consultar todos os documentos na coleção, você chama o Employee.objects.all(). Observe que para acessar o ID de objeto único, usa-se o "id" em vez de "_id".


Um blog de amostra

Agora, um blog simples chamado Blongo será criado. O Python 1.7, Django 1.3, MongoDB 1.8.2, MongoEngine 0.4 e Hypertext Markup Language (HTML) 5 serão usados. Se deseja recriar minhas configurações exatas, eu usei o Ubuntu Linux com o FireFox. O Blongo exibe qualquer entrada de blog inserida no carregamento da página e permite a atualização e exclusão de qualquer entrada —em outras palavras, todas as operações CRUD padrão. As visualizações do Django possuem três métodos: index, update e o delete.

As definições em Cascading Style Sheets (CSS) seguem em um arquivo estático separado. Não entrarei em detalhes aqui, mas fique à vontade para explorar o código fonte incluso em Download.

Considerando que tudo esteja instalado e funcionando bem, crie um novo projeto Django e os componentes necessários, conforme mostrado na Listagem 13.

Lista 13. Comandos para configuração do projeto de blog Django
$ django-admin.py startproject blongo
$ cd blongo
$ django-admin.py startapp blogapp
$ mkdir templates
$ cd blogapp
$ mkdir static

Como novidade para o Django 1.3, há um aplicativo contribuído incluso para uma melhor manipulação de arquivos estáticos. Ao incluir um diretório estático a qualquer diretório do aplicativo (como blogapp, neste caso) e certificar-se de que o django.contrib.staticfiles esteja incluído nos aplicativos instalados, o Django é capaz de localizar arquivos estáticos, como arquivos .css e .js, sem a necessidade de quaisquer ajustes adicionais. A Listagem 14 mostra as linhas dos arquivos de configuração que foram alteradas (a partir do arquivo settings.py padrão) para obter o aplicativo de blog em execução.

Lista 14. Linhas dos arquivos de configuração que foram alteradas (a partir do arquivo settings.py padrão)
# 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',
)

Você possui três modelos neste projeto: index.html, update.html e delete.html. A Listagem 15 mostra o código para todos os três arquivos de modelo.

Lista 15. Código para os arquivos de modelo index.html, update.html e delete.html
<!-- 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>
      <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>

Em seguida, altere os mapeamentos de URL para o código mostrado na Listagem 16, que aponta para as visualizações para o índice, atualização e exclusão. Se você deseja que o blog de amostra crie novas entradas de blog (no índice), atualize as postagens de blog existentes e as exclua quando desejar. Cada ação é realizada pela postagem para uma URL específica.

Lista 16. Mapeamentos de URL para índice, atualização e exclusão
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'),
)

Observe que não é necessário executar o comando syncdb do Django. Para integrar o MongoDB ao seu aplicativo, é necessário o MongoEngine. No arquivo models.py do diretório blogapp, inclua o código mostrado na Listagem 17 .

Lista 17. Incluindo MongoEngine na camada de dados
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)

O nome do banco de dados é obtido a partir do arquivo de definições para separar os interesses. Cada postagem do Blog contém três campos obrigatórios: title, content e o last_update. Se você comparar e contrastar esta listagem com o que você faria normalmente no Django, a diferença não será enorme. Em vez de ter uma classe que herda do django.db.models.Model, esta listagem usa a classe mongoengine.Document em seu lugar. A diferença entre os tipos de dados não será detalhada aqui, mas sinta-se a vontade para verificar os documentos do MongoEngine (consulte Recursos).

Tabela 2 lista os tipos de campo MongoEngine e mostra o tipo de campo Django ORM equivalente, se houver.

Tablela 2. Tipos de campo MongoEngine e Django ORM equivalentes
Tipo de campo MongoEngineDjango ORM equivalente
StringFieldCharField
URLFieldURLField
EmailFieldEmailField
IntFieldIntegerField
FloatFieldFloatField
DecimalFieldDecimalField
BooleanFieldBooleanField
DateTimeFieldDateTimeField
EmbeddedDocumentFieldNenhum
DictFieldNenhum
ListFieldNenhum
SortedListFieldNenhum
BinaryFieldNenhum
ObjectIdFieldNenhum
FileFieldFileField

Finalmente, é possível configurar as suas visualizações. Seguem três métodos de visualização: index, update e o delete. Para executar a ação desejada, deve ser feita uma solicitação de postagem para a URL específica. Por exemplo, para atualizar um documento deve ser feita uma postagem para localhost:8000/update. Executar uma solicitação http 'GET' não salvará, atualizará e assim por diante. As novas postagens do blog são inseridas a partir da visualização de índice. Listagem 18 mostra as implementações para as visualizações de índice, atualização e exclusão.

Lista 18. As visualizações do Django
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):
    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))

Você deve ter notado as instruções eval usadas para recuperar os IDs do documento. Isso é usado para evitar a necessidade de gravar a instrução if mostrada na Listagem 19.

Lista 19. Forma alternativa de recuperar o ID do documento
    if request.method == 'POST':
        id = request.POST['id']
    elif request.method == 'GET':
        id = request.GET['id']

Também é possível escrever dessa maneira. Isso é tudo o que preciso para ter um blog simples ativo e funcionando. Obviamente, faltam muitos componentes para um produto final, tais como usuários, um login, tags e assim por diante.

Conclusão

Como você pode ver, não há realmente muito para chamar o MongoDB a partir do Django. Neste artigo, apresentei rapidamente o MongoDB e expliquei como acessá-lo e manipular suas coleções e documentos a partir do Python por meio do wrapper PyMongo e o mapeador de objeto para documento MongoEngine. Finalmente, ofereci uma rápida demonstração de como criar um formulário CRUD básico usando o Django. Embora esta seja apenas a primeira etapa, espero que você entenda agora como aplicar esta configuração em seus próprios projetos.


Download

DescriçãoNomeTamanho
Sample Django application with MongoEngineblongo.zip12KB

Recursos

Aprender

Obter produtos e tecnologias

  • Saiba mais e faça o download do MongoDB.
  • Faça o download e explore o Django.
  • Visite o website Python para obter os downloads e a documentação.
  • Confira MongoEngine.
  • Aprofunde-se em PyMongo.
  • Avalie produtos IBM da maneira que for melhor para você: faça download da versão de avaliação de um produto, avalie um produto on-line, use-o em um ambiente de nuvem ou passe algumas horas na SOA Sandbox aprendendo a implementar Arquitetura Orientada a Serviços de forma eficiente.

Discutir

  • Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks, enquanto explora os blogs, fóruns, grupos e wikis orientados ao desenvolvedor.

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que acessar o developerWorks, um perfil será criado para você. Informações do seu perfil (tais como: nome, país / região, e empresa) estarão disponíveis ao público, que poderá acompanhar qualquer conteúdo que você publicar. Seu perfil no developerWorks pode ser atualizado a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=800572
ArticleTitle=Usando o MongoDB com Django
publish-date=03082012