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におきましては、こいつらには関係ない作りになってます。ってことですね。
いやー勉強になりました。

, ,

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はしないで個別に呼クエリ投げてるの。
なんでーなんでだー
あたしのやろうとしていることのレベル感でいくと、そんなもんはどっちでも結果同じなんだけど、気になるね。
というところまで今日は進んだのでした。
ネック解消したおかげで、ちょっと気分が軽い。

,

TOP