SQLAlchemyとSQLiteで日付を取り扱う

Filed in Python | SQLAlchemy Leave a comment

あたくしの脳にぼんやりと刻まれている情報によれば、SQLiteは文字列と数値しか扱えないはず。
日付どうする。いやセンセ、日付ぐらいは扱ってくれてもいいのでは。
さておき、SQLAlchemyを通した場合に、なにか賢い仕組みがあるはず。

SQLAlchemyのマニュアルの当該ページには、

Date and Time Types

SQLite does not have built-in DATE, TIME, or DATETIME types, and pysqlite does not provide out of the box functionality for translating values between Python datetime objects and a SQLite-supported format. SQLAlchemy’s own DateTime and related types provide date formatting and parsing functionality when SQlite is used. The implementation classes are DATETIME, DATE and TIME. These types represent dates and times as ISO formatted strings, which also nicely support ordering. There’s no reliance on typical “libc” internals for these functions so historical dates are fully supported.

超訳:
SQLiteは日付型持ってない。SQLAlchemyが独自にこさえてる日付とかの型は、日付フォーマット+SQLiteで使った場合に動作するようになってる。その対象になってるのはDATETIME、DATEおよびTIMEだ。
これらを使う場合に限っては順番とかもちゃんと正しく動くし、ISOフォーマットされた文字列として日付と時刻を表現する。
で、こいつらはlibc内の関数に依存していないので、歴史的な日付が完全にサポートされている。

うわー自信ない。特に最後の一行。逆に読んでたらどうしよう。だって「libc非依存なのでヒストリカルデートが完全にサポートされてる」って、前後つながってる感がない。
だいたい、libcってなんだ。cのライブラリか。

ちょろっと見るとORACLE様の変なページが見つかり、「表 2–10 libc の日付と時間の処理関数」というところで以下の3つの関数の記述がある。

  • getdate() ユーザー形式の日付と時間を変換する
  • strftime() 日付と時間を文字列表現に変換する。
  • strptime() 日付と時間の変換

ははあ。こいつら完全にあたし見たことあるわ、phpで。phpのあの関数ってこいつらをラップしてたんだろか。
見てみる。
こんなページがあった。
PLEAC-PHP(http://pleac.sourceforge.net/pleac_php/datesandtimes.html)

これによると、phpはいろんな方法で日付のサポートをしていて、

  • UNIX/Cライブラリベース
    (localtime, gmtime, strftime, strptime, mktime, time, getdate, gettimeofday)
  • PHPのネイティブ関数(date,strtotime)
  • DateTimeクラスベース

日付の扱い方にこういう種類があるんだよ、とある。
phpのstrftimeとかははラッパーだったんだー、いやそういう感じのことなんだろうなとは思っていたがちゃんと理解した。
Library [libc]-based routinesってことは、Cのライブラリってのがlibcってことでいいんだな。なので、SQLAlchemy+SQLiteにおきましては、こいつらには関係ない作りになってます。ってことですね。
いやー勉強になりました。

, ,

そしてfuelphpやるかとおもいきやpyramidでセットアップ

Filed in Jinja | Nginx | Pyramid | Python | SQLAlchemy Leave a comment

phpめんどくさいよ。仕事で使って、なんで要件好きに決められる自分のプロジェクトでも自らphp選択するんだよ。いみがわからない。
ということで、前の記事でphpをアレしてたのは一旦放っておき、愛するPyramidの環境をつくろうと思う。
Nginx+Python+Mysql、Pyramid+Jinja2。個人的にはmaaDBはなんでもいいですけどこの構成が好きです。
まともにアプリ書き終えるとこまで行ったことないですけどね。

pcreateでざざっと作ったあとで思い出す。
昔の記事で、このへんの構成を好みで動かしたやつがあった気がする。プロジェクトテンプレートを作っておけばよかった。手作業はまたディレクトリ指定間違えたりする凡ミスの温床なのよ。

Leí pylons.config en model/__init__.py

Filed in Pylons | Python | SQLAlchemy Leave a comment

備忘のためのメモ。
結論から書くと読めない。ConfigParserでdevelopment.iniとかを読み込むタイミングより先にmodelの__init__.pyを見に行ってるんだろうなー
model以下に別途hoge.pyみたいにmodelファイル作って使うぶんにはだいじょうぶ。

どうでもいいけど大航海時代やめちゃったから、もうNDI開発ブログじゃないんだよね。名前変えるかな。

Pylons1.0+AuthKit websetup.py

Filed in AuthKit | Pylons | SQLAlchemy Leave a comment

Chapter 19: SimpleSite Tutorial Part 3をみながらAuthKitをセットアップしようとしていてはまっていた。
DBにテーブルが生成されないの。
んでわかったのでメモ。
もとから書いてある

Base.metadata.create_all(bind=Session.bind,checkfirst=True)

は、AuthKitのテーブルは作ってくんない。自分でcreate_allを別に呼ばないといけない。

websetup.py

from myapp import model
users = UsersFromDatabase(model)
model.meta.metadata.create_all(bind=Session.bind,checkfirst=True)

これで

paster setup-app development.ini

できた。やー長かった。解けてみると当たり前なんだけどなー。

追記。

log.info("Adding roles and uses...")

users.role_create("delete")
users.user_create("foo", password="bar")
users.user_create("admin", password="opensesame")
users.user_add_role("admin", role="delete")

このあと

Session.commit()

これがいる。

自己参照

Filed in Pylons | Python | SQLAlchemy Leave a comment

自己参照のrelation @ sqlalchemy 0.5(sklave)
これか?既に試したきはする。。。
問題は、こーやって参照しに行く先が、あたしの場合は3個もあって、常に3個なわけではないということだなー

けど、愚直に見っけたもんをいちいちやるしかないのだ。あたしは変なトコをすっとばして悩む癖あるからな。
エンジンが書き出してくれてるところを気づかずに勝手に一行消して「うごかねえぇぇ」とか言ってた品。

進捗

Filed in NDI | Pylons | Python | SQLAlchemy Leave a comment

一度動いていたはずのアプリを修理・改修しながら再構築するっていうのは、なんかポンコツのレストア作業をやっているような感覚になりますね。
なおってもポンコツになる予感しかしないのだけれど、ホビーだから!ホビー!

キレイにマッピングできなくてもやもやするというのを1週間近く続けております。
パーツの表現で、親子関係になるんだけど自己参照って言うの?部品の材料をぶら下げる的な構造がきれいに決まらない。なんだーなんでだー
図解つきで英文書いてstackoverflowに投稿しよう。
いわゆる親子関係なんだけど、

部品テーブル
-id
-name
-材料1_id
-材料2_id
-材料3_id

この材料1~3_idには同じく部品テーブルがぶら下がる。
んでこれをsqlalchemyにやらせたいわけなんだけど、
「どのテーブルのこといってるかわっかりませーんプップー」
join(outerjoin)構文では値引っ張れるんだけど、なんかDBの構造を引きずってテンプレまでヨタヨタ歩いてくるのがとても醜い。
というのを英文にする。宿題。

いまはこんな書き方。

Mat1 = aliased(Part)
Mat2 = aliased(Part)
Mat3 = aliased(Part)

Session.query(Part,Mat1,Mat2,Mat3).filter(and_(ShipPart.genre_id==2)).\
                       outerjoin( (Mat1, ShipPart.mat1id==Mat1.id)....

用は満たしてるんだけども、それぞれタプルに分かれてくるし、
Part.Mat1.idとか
Part.Mat1.nameとか、わかるかな!そういう引っ張りかたしたいわけ!

SQLAlchemyでUNIQUE制約

Filed in Pylons | Python | SQLAlchemy Leave a comment

ship_idとskill_idの組がUniqueであるっつうテーブル制約を加えたい。

table_obj= Table('table_a', metadata,
    Column('id',        Integer, primary_key=True),

    Column('ship_id',   Integer, ForeignKey('ship.id') , unique=True),
    Column('skill_id',  Integer, ForeignKey('skill.id'), unique=True),
    )

こうやりがち。でもこれぶー。ship_idとskill_idにそれぞれUnique制約かかっちゃって、何にも入らないテーブルの一丁上がり。

table_obj= Table('table_a', metadata,
    Column('id',        Integer, primary_key=True),

    Column('ship_id',   Integer, ForeignKey('ship.id')),
    Column('skill_id',  Integer, ForeignKey('skill.id')),
    UniqueConstraint('ship_id', 'skill_id', name='ship_skill')
    )

UniqueConstraintってのがあったよ。これで

CREATE TABLE table_a (
        id INTEGER NOT NULL AUTO_INCREMENT,
        ship_id INTEGER,
        skill_id INTEGER,
        delflg SMALLINT NOT NULL,
        created DATETIME,
        updated DATETIME,
        PRIMARY KEY (id),
        FOREIGN KEY(skill_id) REFERENCES mst_optionskill (id),
        FOREIGN KEY(ship_id) REFERENCES ship (id),
        CONSTRAINT ship_skill UNIQUE (ship_id, skill_id)
)

的なアレになる。よかった。

, ,

lazyload & joinedload

Filed in Pylons | Python | SQLAlchemy Leave a comment

#語りかける口調ですがあくまでも自分用のメモです。
へんてこなお任せJoinの仕掛けを、実は帰りがけに見つけておったのですが、なんとかソコまでこぎつけました。
あー定義?だいたい終えたよいちばん大きくて面倒なのは見て見ぬ振りしてるけど。
リレーション?なんか拍子抜けするくらい簡単にできたよ。構造が分かりはじめたらなんのことはないって風だったな。
このあたりは時間とってちゃんとまとめて、あたしみたいなphpを書くのがやっとみたいなパンプキンPGにもSQLAlchemy使える使い始めることくらいはできるようにしてやっからな。
もう$とか->とか書きたくなくなるわ。まあそれはいいや。

タイトルのlazyload と joinedload。説明はよく読まずに使ってみた。Lazyはテキトウなとかそんな意味だったと思うので、適当によろしくやってくれるのであろうと読み取りました心で。joinedloadはジョインしてロードしてくれるんだろうたぶん!!!!

(追記20130301)lazyつうのは遅延評価の事だったのね。必要なときに初めて仕事するよと。

実験に使ったのは、

  • BaseUser
  • NdiUser
  • Server
  • Nation

このよっつ。NDIで使うことを意図しているのでちょっと一般的じゃないですが、UserBasic,UserExtend,Blood,Jenderとかによみかえればいいよ!しるか!
NdiUserってのが、他のテーブルへのキー持ってるの。なんで、NdiUserからjoinしてあれこれ引っ張ってこれるわけだ。
で、session.queryにはそういういろんなオプションの書き方も用意されておるわけだ。
めんどくさい。めんどくさいんだよ……。
で、神パイソニスタさんたちが使う神ORMであらせられるところのSQLAlchemy(以下SA)には、リレーションの設定書いてあるならそれ読んでこっちで適当につないでやるわ、という仕掛けがあるみたいなのね。間違ってたらごめんでも結果はそうだ。

まずはこんな呼び出しをする。

    nu = meta.Session.query(NdiUser).filter_by(id=1).one()
    print nu
    print nu.baseuser
    print nu.server
    print nu.nation

こうすると、nu作るときにSQLクエリ一発、nu.baseuser呼び出す時にもう一発、nu.server呼び出す時にさらに一発投げるのだわ。つどつど、必要なときにSQL投げてんの。なるほどー

で、こんなん。

nu = meta.Session.query(NdiUser).filter_by(id=1).\
    options(lazyload(NdiUser.baseuser)).one()

lazyloadの後ろのカッコの中に、リレーションはってる部品をざらざら並べると、そのようによろしくやってくれるのね。

nu = meta.Session.query(NdiUser).filter_by(id=4).\
    options(lazyload(NdiUser.nation, NdiUser.server, NdiUser.baseuser)).one()

こんなん。lazyloadのとこをjoinedloadにすればまあ結果は同じだけど経緯が違うことがあるみたい。
結果はというと、指定したテーブルに関してはあらかじめクエリ飛ばすわけだから値を持ってる状態になると。
取り出すjoin先のテーブルが分かりきってる場合はそのほうがいいよね。

で、SQL文とかをechoさせながら動作見てたら、
joinedloadはテーブル一個だけつなぐ指定のときはSQL側でもjoinでつなげたクエリ発行してて、
つなぐテーブルが二個以上になるとjoinやめて個別にクエリ投げてるの。
lazyloadは連結したいテーブルの数に関わらず、joinはしないで個別に呼クエリ投げてるの。
なんでーなんでだー
あたしのやろうとしていることのレベル感でいくと、そんなもんはどっちでも結果同じなんだけど、気になるね。
というところまで今日は進んだのでした。
ネック解消したおかげで、ちょっと気分が軽い。

,

わかってきたSQLAlchemy

Filed in Database | Pylons | SQLAlchemy Leave a comment

あたし英語テンでだめなんですが、そんなこと言ってられないので読んでるわけ。SQLAlchemyのドキュメントね。
何度も何度も同じところを読んでるうちに、なんとなーく、おぼろげーに、
「あ、そういうこと?」
みたいなのが分かってくる。分かんないトコだらけなんだけど、やっこさんの言いたいことの欠片が見え始めてきて、少しずつわくわくする。
元はといえばオレ解釈でリレーションがうまくはれないなんなんだこれは泣くぞみたいなのが元凶なのですけど、

  • 独特の書き方を嫌って基礎から試みなかった
  • ORMをどこかでめんどくさいものだと思っていた
  • だってSQL一発書いたら明らかじゃん
  • でもオブジェクトとして把握できないのはやっぱりなー

ごちゃごちゃしてんのね。
さしあたり、Declarative(叙述的つうからありのままってこと?テーブルそのもののイメージに近いということかね)な書き方をいったんあきらめる。基本的な書き方にする。
さんざんmodel書いちゃったが、いったん手戻りだ。
テーブルの定義を書いて、モデルの定義を書いて、ねーこれとこれをつなげるためにMapperがあるんだよーというシンプルな構造でやりなおしてみよう。
楽をしようとして足元おろそかにしたがゆえに遠回りをしてしまった。
そらそうだよなー頭のいい人たちが良い形だと考えてつくってるもんをすっ飛ばして、おいしいトコ取りはうまくいかんよなー
行き帰りの道中じっくりドキュメント読めたおかげで、おぼろげながらわかってきているのだ。
なんかわかったらかく。

websetup.py

Filed in Pylons | Python | SQLAlchemy Leave a comment

Pylonsで作ったアプリの初期化にはwebsetup.pyをつかう。
コマンドは
paster setup-app development.ini
これね。
websetup.pyのなかみはこんなかんじ

# -*- coding: utf-8 -*-
"""Setup the ndi application"""
import logging

import pylons.test

from sqlalchemy import *
from ndi.config.environment import load_environment
from ndi.model.meta import Session, Base

log = logging.getLogger(__name__)

def setup_app(command, conf, vars):
    """Place any commands to setup ndi here"""
    # Don't reload the app if it was loaded under the testing environment
    if not pylons.test.pylonsapp:
        load_environment(conf.global_conf, conf.local_conf)

    # Create the tables if they don't already exist
    Base.metadata.create_all(bind=Session.bind)

    #あとでimportの後は*にするが定義終わってないのもあるため
    #作成途中なのでいっこずつ付け足しては投入しております
    from ndi.model.member import BaseUser,NdiUser
    from ndi.model.master import Server,Nation,OptionSkill,SeaAreaL,SeaAreaS,Port

    #テーブル作成
    BaseUser.metadata.create_all(bind=Session.bind)
    NdiUser.metadata.create_all(bind=Session.bind)

    Server.metadata.create_all(bind=Session.bind)
    Nation.metadata.create_all(bind=Session.bind)
    OptionSkill.metadata.create_all(bind=Session.bind)
    SeaAreaL.metadata.create_all(bind=Session.bind)
    SeaAreaS.metadata.create_all(bind=Session.bind)
    Port.metadata.create_all(bind=Session.bind)


    from ndi.model import meta

    #マスタ系のデータ入れてくお
    #Server
    try:
        meta.Session.add(Server(id=1,name='notos'   ))
        meta.Session.add(Server(id=2,name='zephyros'))
        meta.Session.add(Server(id=3,name='euros'   ))
        meta.Session.add(Server(id=4,name='boreas'  ))
        meta.Session.commit()
    except Exception,e:
        print 'Abort:Server'
        meta.Session.rollback()

以下略。このあと延々データ投入が続く。
親の仇みたいに定義書きまくってpaster setup-app development.iniすると、画面が滝のようになってきもちいいいいいいいい
あまりに楽しいので、DBのテーブル全部消す→paster setup-app development.ini→気持ちいいいいいい→DBのテー(略
とかやってます。作業すすみません。進めよ。

,

TOP