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. Mas isso não é problema, por é fácil fazê-lo. 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.
var x = "0";
x === 0;
typeof({});
Lista 1. Comandos JavaScript de amostra que podem ser testados com o MongoDB.
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.
var person1 = {name:"John Doe", age:25};
var person2 = {name:"Jane Doe", age:26, dept: 115};
Lista 2. Exemplos de criação de documento.
Agora, execute os comandos mostrados na Listagem 3 para criar uma nova coleção.
db.employees.save(person1);
db.employees.save(person2);
Lista 3. Criando coleções.
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.
> db.employees.find();
// returns
[
{ "_id" : { "$oid" : "4e363c4dcc93747e68055fa1" },
"name" : "John Doe", "age" : 25 },
{ "_id" : { "$oid" : "4e363c53cc93747e68055fa2" },
"name" : "Jane Doe", "dept" : 115, "age" : 26 }
]
Lista 4. Uma consulta MongoDB simples.
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.
> db.employees.find({name: "John Doe"});
// returns
[
{ "_id" : { "$oid" : "4e363c4dcc93747e68055fa1" },
"name" : "John Doe", "age" : 25 }
]
Lista 5. Consulta por um parâmetro de pesquisa.
Para consultar os funcionários com idade acima de 25 anos, execute o comando mostrado na Listagem 6.
> db.employees.find({age:{'$gt':25}});
// returns
[
{ "_id" : { "$oid" : "4e363c53cc93747e68055fa2" },
"name" : "Jane Doe", "dept" : 115, "age" : 26 }
]
Lista 6. Consulta pelos funcionários com idade superior a 25 anos.
O $gt é um operador especial que significa maior do que. A Tabela 1 lista alguns outros modificadores.
Modificador | Descrição |
---|---|
$gt | Maior que |
$lt | Menor que |
$gte | Maior ou igual a |
$lte | Menor ou igual a |
$in | Verificar a existência de um array, semelhante ao operador ‘in’ de SQL. |
Tablela 1. Os modificadores que podem ser usados com o MongoDB.
É possível, evidentemente, atualizar um registro usando o método update() . É possível, também, todo ele, conforme mostrado na Listagem 7.
> db.employees.update({
name:"John Doe", // Document to update
{name:"John Doe", age:27} // updated document
});
Lista 7. Atualizar um registro todo.
Como alternativa, é possível atualizar apenas um único valor usando o operador $set , conforme mostrado na Listagem 8 .
> db.employees.update({name:"John Doe",
{ '$set': {age:27} }
});
Lista 8. Atualizar um valor único em um registro.
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.
> db.employees.remove({"name":"John Doe"});
> db.employees.find();
// returns
[
{ "_id" : { "$oid" : "4e363c53cc93747e68055fa2" }, "name" : "Jane Doe",
"dept" : 115, "age" : 26 }
]
Lista 9. Remover o “John Doe” da coleção de funcionários.
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. A 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.
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"])
Lista 10. Sessão de PyMongo de amostra.
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.
for e in employees.find({"name":"John Doe"}):
print e
Lista 11. Executar uma consulta com o PyMongo.
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.
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"]
Lista 12. Sessão de exemplo do MongoEngine.
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.
$ django-admin.py startproject blongo
$ cd blongo
$ django-admin.py startapp blogapp
$ mkdir templates
$ cd blogapp
$ mkdir static
Lista 13. Comandos para configuração do projeto de blog Django.
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.
# 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',
)
Lista 14. Linhas dos arquivos de configuração que foram alteradas (a partir do arquivo settings.py padrão).
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.
<!-- 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>
Lista 15. Código para os arquivos de modelo index.html, update.html e delete.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.
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('',
url(r'^