Flaskの拡張ライブラリFlask-SQLAlchemyを使って配列型のカラム定義をする。このとき、SQLはPostgreSQLのみが対応しているため、必然的にDB接続時にはpsycopg2を利用することになる。
from flask import Flask from flask_sqlalchemy import SQLAlchemy from sqlalchemy import ARRAY from sqlalchemy.ext.mutable import MutableList app = Flask(__name__) app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql+psycopg2://postgres:postgres@127.0.0.1/sample" db = SQLAlchemy() db.init_app(app) class Sample(db.Model): __tablename__ = "sample" id = db.Column(ARRAY(db.Integer,as_tuple=True,dimensions=1),primary_key=True) name = db.Column(db.String(100),default="") arr = db.Column(MutableList.as_mutable(ARRAY(db.Text)))
問題①
配列型はプライマリーキーとしてあまり、適さないということである。SQLAlchemyでクエリを発行する際に、そのモデルクラスのプライマリーキーのカラムに相当するdb.Columnオブジェクトのデータをハッシュ化する。配列型をプライマリーキーにするとこのタイミングでSQLAlchemy側でエラーが発生する。これは、内部的にPythonリストとしてデータを保持しているからである。これを回避するには、ARRAY型を定義する際に「as_tuple=True」として内部で保持しているデータをリストではなくタプルに設定しておく必要がある。
本家のドキュメントではこのことはあまり触れていない。正直ここにたどり着くまでに時間かかりすぎたのでメモしている。
問題②
ARRAY型のデータを更新するのはデフォルトだと不便。ARRAY型を定義するくらいなので、データを追加、更新、削除するくらいのことはしたいだろうが、そのようなメソッドはARRAY型カラムオブジェクトにはない。そこでMutableListインプリメントを利用する。MutableListインプリメントには、追加、更新、削除に対応するメソッドが存在する。しかし、このインプリメントを実装したクラスは当然プライマリーキーに指定してはならない。更新可能であること=ハッシュ不可能オブジェクトであるからだ。
本家のドキュメントは概要だけ説明してある。多分リストのメソッドをそのまま使えるようにした程度だからであろう。そして、2次元以上の配列型に対しての操作は自分でサブクラスを定義するべきだと書いてあるので、「深い」変更は難しいそうだ。そもそもDBで深いデータ構造を1カラムで扱うべきではないだろう。。。という気もするが。