概要
Python の ORM を調査中。今回はWebアプリケーション作成で比較的利用されている「Django」内蔵のORMを調査してみる。
Djangoに関して
- 公式サイト:https://www.djangoproject.com/
- pypi:http://pypi.python.org/pypi/Django/
- ライセンス:BSD
- 最新:1.4.1(2012-07-30)
- Python3対応:×
- 対応RDB:MySQL,Oracle,PostgreSQL,SQLite3
「Django」はPythonで代表的なフルスタックフレームワーク。
結構なんでも揃い、かつプラグイン機構が充実しているので、拡張もしやすい。
ライセンスは BSD。Python 3 には今の所対応していない。
対応RDBは MySQL、Oracle、PostgreSQL、SQLite。
本来は ORM だけ独立で使うための物ではないかもしれないが、今回は ORM だけを調査したい。
ドキュメント
「Django ドキュメント」から日本語のドキュメントが読める。
今回のようにORMだけ独立で利用したい場合等は公式ドキュメント(英語)の「Writing custom django-admin commands」に書いてある。使い方は昨日書いたので参照。
インストール
pip でインストールする。
pip install django
サンプルソース
自分は ORM を利用する場合あまり DDL を発行しないので、DML を中心としたサンプルとする。
今回は MySQL でデータベースとテーブル用意する。
> mysql -u root
mysql> CREATE DATABASE example DEFAULT CHARACTER SET utf8;
mysql> GRANT ALL PRIVILEGES ON example.*
-> TO username@localhost
-> IDENTIFIED BY 'password';
mysql> exit;
> mysql -u username -p example
mysql> CREATE TABLE tweets (
-> id serial PRIMARY KEY,
-> status_id VARCHAR(255) UNIQUE NOT NULL,
-> from_user_id VARCHAR(255) NOT NULL,
-> text VARCHAR(140) NOT NULL,
-> created_at VARCHAR(50) NOT NULL,
-> datetime DATETIME NOT NULL
->) engine=innodb default charset=utf8;
次に付属のツール「django-admin.py」を利用してプロジェクトの雛形を作成する。
「django-admin.py」は pip 等でインストールすると、適切な場所にインストールされるのでパスを通しておくと良い。
以下のように利用する。
django-admin.py startproject mysite
データベースの設定は「settings.py」で行なう。HOST、PORT は初期設定のままなので、今回は空にしておく。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql.',
'NAME': 'example',
'USER': 'username',
'PASSWORD': 'password',
'HOST': '',
'PORT': '',
}
}
「settings.py」には他にも以下のような設定をした。
TIME_ZONE = 'Asia/Tokyo'
LANGUAGE_CODE = 'ja-JP'
USE_TZ = False
次にアプリケーションを作成する。
python manage.py startapp example
「management/commands」ディレクトリを作成する。
mkdir -p example/management/commands
touch example/management/__init__.py
touch example/management/commands/__init__.py
「settings.py」の「INSTALLED_APPS」にアプリケーションを有効化するために追加する。
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# 以下を追加
'example',
)
「models.py」を以下のように記述。(実際には __unicode__ 等を書くが省略)
from django.db import models
class Tweets(models.Model):
status_id = models.CharField(max_length=255)
from_user_id = models.CharField(max_length=255)
text = models.CharField(max_length=140)
created_at = models.CharField(max_length=50)
datetime = models.DateTimeField()
class Meta:
db_table = 'tweets'
「example/management/commands」以下にスクリプトを作成。sample.py」の名前で以下のように作成。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.core.management.base import BaseCommand
from datetime import datetime
from example.models import Tweets
class Command(BaseCommand):
def handle(self, *args, **options):
# 基本的に autocommit=True
tweet = Tweets(
status_id='251298602096xxxxx1',
from_user_id='43172xxx1',
text=u'さんぷるでーた',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
# インスタンスの生成では INSERT しない
self.assertEqual(0, Tweets.objects.count())
# save メソッドで INSERT を発行
tweet.save()
self.assertEqual(1, Tweets.objects.count())
tweet = Tweets(
status_id='251298602096xxxxx2',
from_user_id='43172xxx2',
text=u'さんぷるでーた その 2',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
)
tweet.save()
self.assertEqual(2, Tweets.objects.count())
# UPDATE 前の確認
self.assertEqual(1, Tweets.objects.filter(
from_user_id='43172xxx1').count())
self.assertEqual(1, Tweets.objects.filter(
from_user_id='43172xxx2').count())
self.assertEqual(0, Tweets.objects.filter(
from_user_id='43172xxx3').count())
# UPDATE
tweet = Tweets.objects.get(from_user_id='43172xxx2')
tweet.from_user_id = '43172xxx3'
# COMMIT
tweet.save()
# UPDSTE 後の確認
self.assertEqual(0, Tweets.objects.filter(
from_user_id='43172xxx2').count())
self.assertEqual(1, Tweets.objects.filter(
from_user_id='43172xxx3').count())
# bulk INSERT
insert_lst = [
Tweets(
status_id='251298602096xxxxx3',
from_user_id='43172xxx3',
text=u'さんぷるでーた その 3',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
),
Tweets(
status_id='251298602096xxxxx4',
from_user_id='43172xxx4',
text=u'さんぷるでーた その 4',
created_at='Mon, 1 Oct 2012 12:33:50 +0000',
datetime=datetime.utcnow(),
),
]
Tweets.objects.bulk_create(insert_lst)
self.assertEqual(4, Tweets.objects.count())
# DELETE
for tweet in Tweets.objects.all():
tweet.delete()
# DELETE 確認
self.assertEqual(0, Tweets.objects.count())
def assertEqual(self, first, second):
"""
テスト用の簡易関数
"""
if first == second:
print '・',
else:
print 'NG',
実行は以下のようにする。
python manage.py sample
まとめ
DjangoのORMは確かに良くできている。Djangoを普段利用している人には便利だろう。
ただ、速度はあまり早くないので、速度をそれほど必要としないような管理ツール等に向いていると思われる。
2012/10/16追記:
この記事は Python のORM 調査の記事の一部となる。以下が関連記事。