pyramidでmongoengineを使ってみる

pythonのmongodb用ODM(ORM)であるmongoengineのお話です。
mongoengineでmongoのデータを更新するフォームの勉強をしたかったので、mongoengineを軽く触ってみました。

1. 環境構築

今回のテスト用にmyform環境を作成し「/form」をルーティングに追加します。
ちなみにSymfony2でtwigを使ってたのでテンプレートエンジンには書式が同じjinja2を設定してます。

// 必要モジュールのインストール
$ pip install pyramid==1.3.4
$ pip install waitress
$ pip install pyramid_jinja2
$ pip install mongoengine
// プロジェクト作成
$ pcreate -s starter myform
$ cd myform
$ python setup.py develop
// 「/form」をルーティングにしてテンプレートエンジンにjinja2を設定
$ vi myform/__init__.py
from pyramid.config import Configurator
from mongoengine    import connect

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    config = Configurator(settings=settings)
    config.include('pyramid_jinja2')
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('form_index', '/form')
    config.scan()

    connect('pyramid_form')
    return config.make_wsgi_app()

2. コレクション用のモデルを作成

「email」「first_name」「last_name」の文字列型のカラムを持った「user」コレクションのモデルを定義します。

$ mkdir myform/models
$ touch myform/models/__init__.py
$ vi myform/models/user.py
from mongoengine import Document
from mongoengine import StringField

class User(Document):
    email      = StringField(required=True)
    first_name = StringField(max_length=50)
    last_name  = StringField(max_length=50)

3. データを挿入するviewを作成

Userモデルのインスタンスを生成して保存するビューとテンプレートを作成

// データを挿入するview
$ vi myform/views.py
from pyramid.view import view_config
from models.user import User

@view_config(route_name='form_index', renderer='templates/form/index.jinja2')
def my_view(request):
    test = User(email='test1@example.com')
    test.first_name  = 'test1_fname'
    test.last_name   = 'test1_lname'
    test.save()

    return {'value':'test!'}

// テンプレート作成 
$ mkdir  myform/templates/form/
$ vi myform/templates/form/index.jinja2
hello {{ value }}

4. サーバを起動して確認

サーバを起動後「/form」にアクセスすると「hello test!」と表示されます。
mongodbにデータが保存されている事が確認できます。

$ mongo
MongoDB shell version: 2.4.5
connecting to: test
> show dbs;
local   0.078125GB
pyramid_form    0.203125GB
> show collections
system.indexes
user
> var x = db.user.findOne();
> printjson(x)
{
        "_id" : ObjectId("52345cfe2e5229637bcaa389"),
        "email" : "test1@example.com",
        "first_name" : "test1_fname",
        "last_name" : "test1_lname"
}

5. EmbeddedDocumentの使用

mongodbの特性の1つにEmbeddedDocument機能があるので使ってみました。
「post」コレクションにCommentという埋め込みドキュメントを設定します。

$ vi myform/models/posts.py
# --* encoding:utf-8 *--

from mongoengine import Document
from mongoengine import EmbeddedDocument
from mongoengine import StringField
from mongoengine import ReferenceField
from mongoengine import ListField
from mongoengine import EmbeddedDocumentField
from .user       import User

class Comment(EmbeddedDocument):
    content = StringField()
    name    = StringField(max_length=120)

class Post(Document):
    title    = StringField(max_length=120, required=True)
    author   = ReferenceField(User)
    tags     = ListField(StringField(max_length=30))
    comments = ListField(EmbeddedDocumentField(Comment))

    meta     = {'allow_inheritance': True}

class TextPost(Post):
    content = StringField()

class ImagePost(Post):
    image_path = StringField()

class LinkPost(Post):
    link_url = StringField()

// TextPostとLinkPostの2つのインスタンスを作成して保存
$ vi myform/views.py 
from pyramid.view import view_config
from models.user  import User
from models.posts import TextPost
from models.posts import LinkPost
from models.posts import Comment

@view_config(route_name='form_index', renderer='templates/form/index.jinja2')
def my_view(request):
    user = User(email='test2@example.com', first_name='test2_fname', last_name='test2_lname').save()

    comment = Comment(content='comment_content', name='comment_name')

    post1 = TextPost(title='Fun with MongoEngine', author=user)
    post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
    post1.tags = ['mongodb', 'mongoengine']
    post1.comments = [comment]
    post1.save()

    post2 = LinkPost(title='MongoEngine Documentation', author=user)
    post2.link_url = 'http://docs.mongoengine.com/'
    post2.tags = ['mongoengine']
    post2.save()


    return {'value':'test!'}

6. EmbeddedDocumentの確認

mongoで確認するとauthorでUserとの関連が設定されて、commentsにコメントが埋め込まれている事が確認できます。

> db.post.find().forEach(printjson);
{
        "_id" : ObjectId("52345fda2e52296395f90bdf"),
        "_cls" : "Post.TextPost",
        "title" : "Fun with MongoEngine",
        "author" : ObjectId("52345fda2e52296395f90bde"),
        "tags" : [
                "mongodb",
                "mongoengine"
        ],
        "comments" : [
                {
                        "content" : "comment_content",
                        "name" : "comment_name"
                }
        ],
        "content" : "Took a look at MongoEngine today, looks pretty cool."
}
{
        "_id" : ObjectId("52345fda2e52296395f90be0"),
        "_cls" : "Post.LinkPost",
        "title" : "MongoEngine Documentation",
        "author" : ObjectId("52345fda2e52296395f90bde"),
        "tags" : [
                "mongoengine"
        ],
        "comments" : [ ],
        "link_url" : "http://docs.mongoengine.com/"
}

さすがODMって感じです。

参考

http://docs.mongoengine.org/en/latest/tutorial.html