読者です 読者をやめる 読者になる 読者になる

DjangoでSQLAlchemy

SQLAlchemy Django

今日、Pyhton焼肉に行ったときに、id:soundkitchenさんから、DjangoでSQLAlchemyを使う方法について質問を受けたのですが、あとで見返してみても、以前書いたこと以上のことは、実際やっていません・・・


http://d.hatena.ne.jp/perezvon/20070926/1190823332


例えば、Djangoプロジェクト名をgumiと作り、そのプロジェクトが使う唯一のデータベース接続を表現するパッケージを、gumi/db.pyとして表現するとすると、

# -*- coding: utf-8 -*-
from django.conf import settings
from django.core import signals
from django.dispatch import dispatcher

import sqlalchemy
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.engine.url import URL

__all__ = ['Session', 'metadata']

def create_engine():
    url = URL(drivername=settings.DATABASE_ENGINE,
              database=settings.DATABASE_NAME,
              username=settings.DATABASE_USER,
              password=settings.DATABASE_PASSWORD,
              host=settings.DATABASE_HOST,
              port=settings.DATABASE_PORT or None,
              query = getattr(settings, 'DATABASE_OPTIONS', {})
              )

    options = getattr(settings, 'SQLALCHEMY_OPTIONS', {})
    engine = sqlalchemy.create_engine(url, **options)
    return engine

def end_request(signal, sender):
    Session.remove()

dispatcher.connect(receiver=end_request,
                   signal=signals.request_finished)

metadata = sqlalchemy.MetaData()

Session = scoped_session(sessionmaker(autoflush=True,
                                      transactional=True,
                                      bind=create_engine()))

ここで書いているコードが、まさにgu3.jpで動いている、一字一句と違わないSQLAlchemyの接続部分のコードです。


上のようなコードをgumi/db.pyとして定義した上で、モデルの部分で次のように、モデルを定義します。

from sqlalchemy import *
from gumi.db import Session, metadata

some_table = Table('some_table', metadata,
                          Column('id', Integer, primary_key=True),
                          Column('some_value', varchar(250), nullable=False),
                          mysql_engine='InnoDB'
                          )


gu3.jpのテーブル定義とモデル定義の部分は、実際には、次のようなコードのパターンになっています。

from sqlalchemy.orm import *
from gumi.db import Session, metadata

some_table = Table('some_table', metadata,
                          Column('id', Integer, primary_key=True),
                          Column('some_value', String(100), nullable=False,
                          mysql_engine='InnoDB',
                          )

class SomeObject(object):
    pass

mapper(SomeObject, some_table)


ビューでは、次のようなコードでビュー関数として登録しています。

import django.newforms as forms
from gumi.db import Session

class SomeForm(forms.Form):
     # newformを明示的に定義します
     pass

def some_action(req):
     if req.method != "POST":
         form = SomeForm()
     else:
         form = SomeForm(req.POST)
         if form.is_valid():
                 data = form.clean()
                 obj = SomeObject()
                 obj.some_param = data['a']
                 obj.another_param = data['b']
                 Session.save(obj)
                 Session.commit()
                 return HttpResponseRedirect('/')
     return render_to_response('some/template.html')


こういうコートの書き方にすることによって生じる不都合というものも認識していますが、「1アプリケーション1DB接続」という構成ならば、不都合は生じないはず(SQLAlchemy0.4以降ならばこうかくべき)という書き方です。