Django は非常に優れたモジュール方式で使用されるため、Django ベースの Web アプリケーションでは、さまざまなコンポーネントを簡単に置き換えることができます。最近では NoSQL データベースがよく使われるようになっているため、MySQL などの標準的なリレーショナル・データベースとは異なるバックエンドを使用してアプリケーションを実行したい場合があるかもしれません。この記事では、MongoDB について簡単に紹介し、続いて PyMongo または MongoEngine を使用して Python プロジェクトの中で MongoDB を呼び出す方法を説明します。その後で、Django と MongoEngine を使用して CRUD (Create、Read、Update、Delete) 操作を実行できる単純なブログを作成します。
nosql-database.org によれば、NoSQL データベースは「そのほとんどが、非リレーショナル、分散型、オープンソース、水平スケーラビリティーという性質を備えた次世代のデータベース」です。この記事で取り上げるドキュメント指向のデータベース MongoDB は、この NoSQL データベースの 1 つです。
Django 1.3 は、最初から SQLite、MySQL、PostgreSQL、Oracle をサポートしていますが、MongoDB はサポートしていません。MongoDB のサポートを追加するのは簡単ですが、残念なことに MongoDB では自動管理パネルを使用できないという欠点があります。そのため、この欠点と皆さんの要求とを照らして MongoDB のサポートを追加するかどうかを判断する必要があります。
MongoDB は JavaScript インタープリターとして動作するため、データベース操作は JavaScript コマンドを使って行われます。皆さんのマシンに MongoDB をローカルでインストール (「参考文献」を参照) した後、リスト 1 に示すコマンドを試してみてください。
リスト 1. MongoDB で試すことができる JavaScript コマンドの例
var x = "0";
x === 0;
typeof({});
|
MongoDB を使い始める上で JavaScript のエキスパートである必要はありませんが、知っておくと役に立つ JavaScript の概念をいくつか以下に示します。
- オブジェクト・リテラル構文を使用して (つまり 2 つの波括弧を使用して)、オブジェクトを作成することができます (例えば
var myCollection = {};など)。 - 角括弧 (
[]) を使用して配列を作成することができます。 - JavaScript では、数値、ブール値、ヌル、未定義を除くすべてがオブジェクトです。
JavaScript の他の機能 (例えば、プロトタイプ・ベースのオブジェクト指向プログラミング (OOP: Object-Oriented Programming)、スコープの規則、JavaScript が持つ関数型プログラミングの性質など) について学びたい場合には、「参考文献」を参照してください。
リレーショナル・データベースとはまったく対照的に、MongoDB にはスキーマがありません。テーブルの代わりに、複数のドキュメントで構成されるコレクションを使用します。ドキュメントはリスト 2 のようにオブジェクト・リテラル構文を使用して作成されます。
リスト 2. ドキュメントを作成する例
var person1 = {name:"John Doe", age:25};
var person2 = {name:"Jane Doe", age:26, dept: 115};
|
では、リスト 3 のコマンドを実行して新しいコレクションを作成してみましょう。
Creating collections
db.employees.save(person1); db.employees.save(person2); |
MongoDB にはスキーマがないため、person1 と person2 の列の型が同じである必要はなく、さらには person1 と person2
の列の数が同じである必要もありません。また、MongoDB は動的な性質を持つため、上記コードによってエラーはスローされずに employees
が作成されます。ドキュメントを取得するためには find() メソッドを使用します。employees
内のすべてのドキュメントを取得するためには、リスト 4 のように引数なしで find() を呼び出します。
リスト 4. MongoDB の単純なクエリー
> db.employees.find();
// returns
[
{ "_id" : { "$oid" : "4e363c4dcc93747e68055fa1" },
"name" : "John Doe", "age" : 25 },
{ "_id" : { "$oid" : "4e363c53cc93747e68055fa2" },
"name" : "Jane Doe", "dept" : 115, "age" : 26 }
]
|
_id
が主キーと同じ役割をしていることに注意してください。具体的な内容を指定したクエリーを実行する場合、クエリーによって求める対象を示すキーと値のペアを持つ別のオブジェクトを渡す必要があります
(リスト 5)。
リスト 5. 1 つの検索パラメーターによってクエリーを実行する
> db.employees.find({name: "John Doe"});
// returns
[
{ "_id" : { "$oid" : "4e363c4dcc93747e68055fa1" },
"name" : "John Doe", "age" : 25 }
]
|
年齢が 25 歳よりも上の従業員に対するクエリーを実行する場合、リスト 6 のコマンドを実行します。
リスト 6. 年齢が 25 歳よりも上の従業員に対するクエリーを実行する
> db.employees.find({age:{'$gt':25}});
// returns
[
{ "_id" : { "$oid" : "4e363c53cc93747e68055fa2" },
"name" : "Jane Doe", "dept" : 115, "age" : 26 }
]
|
$gt は「・・・よりも大きい」を意味する特殊演算子です。他にもいくつかの修飾子を表
1 に記載します。
表 1. MongoDB で使用できる修飾子
| 修飾子 | 説明 |
|---|---|
| $gt | ・・・よりも大きい |
| $lt | ・・・よりも小さい |
| $gte | 以上 |
| $lte | 以下 |
| $in | 配列内に存在するかどうかのチェック。SQL の in 演算子に似ています。 |
当然ですが、update() を使用してレコードを更新することができます。リスト
7 のようにレコード全体を更新することもできます。
リスト 7. レコード全体を更新する
> db.employees.update({
name:"John Doe", // Document to update
{name:"John Doe", age:27} // updated document
});
|
あるいは、リスト 8 のように $set 演算子を使用して 1 つの値のみを更新することもできます。
リスト 8. レコード内の 1 つの値を更新する
> db.employees.update({name:"John Doe",
{ '$set': {age:27} }
});
|
コレクションを空にするためには、引数なしで remove() を呼び出します。例えば employees
コレクションから John Doe を削除したい場合にはリスト 9 のようにします。
リスト 9. employees コレクションから John Doe を削除する
> db.employees.remove({"name":"John Doe"});
> db.employees.find();
// returns
[
{ "_id" : { "$oid" : "4e363c53cc93747e68055fa2" }, "name" : "Jane Doe",
"dept" : 115, "age" : 26 }
]
|
MongoDB の入門は、これぐらいで十分です。もちろん、引き続き MongoDB の公式サイトを調べても構いません。この公式サイトには、チュートリアルを備えた便利な Web ベースの対話型 mongodb コマンド・プロンプトや、公式のドキュメントが用意されています。「参考文献」を参照してください。
Python または Django から MongoDB にアクセスする方法はいくつかあります。第 1 の方法は PyMongo という Python モジュールを使用する方法です。リスト 10 に PyMongo セッションの例を示します。この例では、MongoDB がインストールされており、あるポートで既に 1 つのインスタンスが実行されていることを前提としています。
リスト 10. PyMongo セッションの例
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
を使用すると複数のデータベースを並行して実行することができます。接続を定義するためには、単純にデータベース名を接続インスタンスに渡します。この場合には、新しいドキュメントの定義を作成するために、Python
の辞書が JavaScript のオブジェクト・リテラルの代わりとなり、Python のリストが JavaScript の配列の代わりとなります。find メソッドはデータベースのカーソル・オブジェクトを返し、このオブジェクトに対して繰り返し処理を行うことができます。
MongoDB と PyMongo は構文が似ているため、MongoDB のコマンドラインと PyMongo でのコマンドの実行とを切り換えるのは簡単です。例えば、 リスト 11 に示すのは PyMongo でクエリーを実行する方法です。
リスト 11. PyMongo でクエリーを実行する
for e in employees.find({"name":"John Doe"}):
print e
|
Python から MongoDB を呼び出す方法には、MongoEngine を使用する方法もあります。Django に組み込みの ORM を使用したことがある人であれば、その ORM と MongoEngine は似ているように思えるはずです。MongoEngine はドキュメントとオブジェクトの間のマッパーであり、概念としては ORM と似ています。リスト 12 に MongoEngine のセッションの例を示します。
リスト 12. 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"]
|
Employee オブジェクトは mongoengine.Document を継承しています。この例では、StringField と IntField という 2
つのフィールド・タイプを使用しています。Django の ORM の場合と同じように、コレクション内のすべてのドキュメントに対してクエリーを実行するためには、Employee.objects.all() を呼び出します。一意のオブジェクト ID にアクセスするためには “_id” ではなく “id“ を使用することに注意してください。
では、Blongo という名前の単純なブログを作成しましょう。ここでは、Python 1.7、Django 1.3、MongoDB 1.8.2、MongoEngine
0.4、そして HTML (HyperText Markup Language) 5 を使用します。私とまったく同じ設定を再現したい人のために紹介すると、私は Ubuntu
Linux と FireFox を使用しました。Blongo
は、ページがロードされると、これまでのブログ記事をすべて表示し、任意の記事を更新、削除できるようにします。つまり標準的なすべての CRUD
操作を行うことができます。Django のビューには、index、update、delete という 3 つのメソッドがあります。
CSS (Cascading Style Sheets) は別の静的ファイルで定義されます。ここでは詳細については触れませんが、「ダウンロード」に含まれているソース・コードを調べてみてください。
すべてが問題なくインストールされ、実行されているとして、リスト 13 のように新しい Django プロジェクトと必要なコンポーネントを作成します。
リスト 13. Django ブログ・プロジェクトを設定するためのコマンド
$ django-admin.py startproject blongo $ cd blongo $ django-admin.py startapp blogapp $ mkdir templates $ cd blogapp $ mkdir static |
Django 1.3 の新機能として、静的ファイルの処理を改善するためのアプリケーションが contrib の中に含まれています。任意のアプリケーション・ディレクトリー (この場合の blogapp など) に static ディレクトリーを追加し、インストールされたアプリケーションに必ず django.contrib.staticfiles を含めるようにすることで、Django は他に何も調整しなくても .css ファイルや .js ファイルなどの静的ファイルを見つけることができるようになります。リスト 14 に、このブログ・アプリケーションを実行させるために (デフォルトの settings.py ファイルから) 変更された設定ファイルの行を示します。
リスト 14. (デフォルトの settings.py ファイルから) 変更された設定ファイルの行
# 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',
)
|
このプロジェクトには index.html、update.html、delete.html というテンプレートもあります。リスト 15 に、この 3 つのテンプレート・ファイルすべてのコードを示します。
リスト 15. 3 つのテンプレート・ファイル、index.html、update.html、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>
|
次に、URL のマッピングをリスト 16 のコードに変更します。このコードは、index、update、delete のビューを指しています。サンプル・ブログでは、必要に応じて新しいブログ記事を (index で) 作成したり、既存のブログ記事を (update で) 更新したり、(delete で) 削除したりすることができるようにします。各アクションを実行するためには、特定の URL に POST します。
リスト 16. index、update、delete のための URL のマッピングe
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'),
)
|
syncdb という Django コマンドを実行する必要がないことに注意してください。MongoDB
をアプリケーションに統合するためには MongoEngine が必要です。blogapp ディレクトリーの models.py ファイルにリスト 17 のコードを追加します。
リスト 17. データ層に MongoEngine を含める
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)
|
関心を分離するために、データベースの名前は settings ファイルから取得します。各ブログ記事には、title、content、last_update という 3 つの必須フィールドがあります。このリスト 17 の内容と Django
で通常実行することを比較対照してみると、それほど大きな違いはありません。django.db.models.Model を継承するクラスの代わりに、このリストでは mongoengine.Document クラスを使用しています。ここではデータ型の違いについては説明しませんが、MongoEngine
のドキュメントを調べてみてください (「参考文献」を参照)。
表 2 は、MongoEngine のフィールド・タイプの一覧と、(それらのタイプと等価な Django の ORM フィールド・タイプがある場合には) その等価な ORM フィールド・タイプを示しています。
表 2. MongoEngine のフィールド・タイプと、それらと等価な Django の ORM フィールド・タイプ
| MongoEngine のフィールド・タイプ | 等価な Django の ORM フィールド・タイプ |
|---|---|
| StringField | CharField |
| URLField | URLField |
| EmailField | EmailField |
| IntField | IntegerField |
| FloatField | FloatField |
| DecimalField | DecimalField |
| BooleanField | BooleanField |
| DateTimeField | DateTimeField |
| EmbeddedDocumentField | 該当なし |
| DictField | 該当なし |
| ListField | 該当なし |
| SortedListField | 該当なし |
| BinaryField | 該当なし |
| ObjectIdField | 該当なし |
| FileField | FileField |
最後に、ビューを設定します。ここでは、index、update、delete という 3
つのビュー・メソッドがあります。目的のアクションを実行するためには、そのための特定の URL に対して POST
リクエストを実行する必要があります。例えば、ドキュメントを更新するためには localhost:8000/update に対して POST を実行します。HTTP の GET リクエストを実行しても、保存 (save) や更新 (update) 等を実行することはできません。新しいブログ記事は index ビューから挿入します。リスト 18 に、index ビュー、update ビュー、delete ビューの実装を示します。
リスト 18. 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))
|
ドキュメントの ID を取得するために eval
文を使用していることにお気付きの読者もいるのではないでしょうか。eval 文を使用する理由は、リスト 19 に示す if 文を書かずに済ませるためです。
リスト 19. ドキュメントの ID を取得するための別の方法
if request.method == 'POST':
id = request.POST['id']
elif request.method == 'GET':
id = request.GET['id']
|
リスト 19 のようなコードにすることもできます。これで、単純なブログを立ち上げて実行状態にするための作業はすべて終わりです。このブログ・アプリケーションを最終的なものとするには、実装する必要があるコンポーネントがまだ数多くあります (ユーザー・コンポーネント、ログイン・コンポーネント、タグ・コンポーネント等々)。
ここまでの説明でおわかりのように、Django から MongoDB を呼び出すのは、まったくたいしたことではありません。この記事では MongoDB について簡単に紹介し、PyMongo ラッパーと、オブジェクトとドキュメントの間のマッパーである MongoEngine とを使用して、Python から MongoDB にアクセスする方法と MongoDB のコレクションとドキュメントを操作する方法について説明しました。最後に、Django を使用して基本的な CRUD フォームを作成する方法を簡単に説明しました。この記事は最初のステップにすぎませんが、ここまでの説明から、この記事の設定を皆さんのプロジェクトに応用する方法を理解できたようであれば幸いです。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Sample Django application with MongoEngine | blongo.zip | 12KB | HTTP |
学ぶために
- Mozilla のチュートリアル「A
re-introduction to JavaScript」と Douglas Crockford 著の『JavaScript: The Good Parts
―「良いパーツ」によるベストプラクティス』(2008年、オライリージャパン刊) を読み、JavaScript について学んでください。
- developerWorks の Open source
ゾーンには、オープンソース技術を使用した開発や、IBM 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
- さまざまな IBM 製品や IT 業界のトピックに焦点を絞った developerWorks
の Technical events and webcasts で最新情報を入手してください。
- 無料の developerWorks
Live! briefing に参加して、IBM の製品およびツールについての情報や IT 業界の動向についての情報を迅速に把握してください。
- Twitter で developerWorks をフォローしてください。
- developerWorks On demand
demos をご覧ください。初心者のための製品インストール方法やセットアップのデモから、上級開発者のための高度な機能に至るまで、多様な話題が解説されています。
製品や技術を入手するために
- MongoDB について学び、また MongoDB をダウンロードしてください。
- Django をダウンロードして試してみてください。
- Python の Web サイトを訪れてください。ダウンロードやドキュメントが豊富に用意されています。
- MongoEngine について調べてください。
- PyMongo について調べてください。
- 皆さんの目的に最適な方法で IBM 製品を評価してください:
製品の試用版をダウンロードする方法、オンラインで製品を試す方法、クラウド環境で製品を使う方法、あるいは
SOA Sandbox で数時間を費やし、サービス指向アーキテクチャーの効率的な実装方法を学ぶ方法などがあります。
議論するために
- developerWorks コミュニティーに参加してください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、ウィキを調べることができます。
