データのプロパティへ動的にアクセスする方法(python,sqlalchemy)


pythonでsqlAlchemyを使ってデータを取得する時に、動的にプロパティへアクセスする方法がわからなかったので調べた方法を残しておきます。
※この記事はPyhtonのgetattr()に関する記事です。

経緯

今回の場合は、以下のようなクエリが2つあってなんとかして1つの関数としてまとめたいと考えていました。

 q = db.query(Account.id, Account.name).filter(
     Account.status != 0,
     account["id"] == Building.account_id
 )
 db_accounts = q.all()

 q = db.query(Building.id, Building.name).filter(
     Building.status != 0,
     building["id"] == Floor.builing_id
 )
 db_buildings = q.all()

なので、とりあえず関数を1つ作ってみることにしました。

    def get_related_data(model, parent_data, child_table_property):
        q = db.query(model.id, model.description).filter(
            model.status != 9,
            parent_data["id"] == model.child_table_property
        )
        return q.all()

  #関数の返り値をdb_biuldingsに代入
  db_buildings = get_related_data(BuildingModel, account, "account_id")

クエリの最後の行で、model.child_table_propertyと書けば今回の場合はBuildingModel.account_idとなってクエリが正しく実行されると思ったのですが、child_table_propertyの部分は実際のプロパティ名を書かないといけないみたいで、うまくいきませんでした。

そこでparent_data["id"]にあるように文字列でプロパティを指定する方法を調べてみたところPyhtonの組み込み関数であるgetattr()メソッドに辿り着きました!

getattr()

pythonのドキュメントには以下のように説明されています。

getattr(object, name[, default])
object の指名された属性の値を返します。 name は文字列でなくてはなりません。文字列がオブジェクトの属性の一つの名前であった場合、戻り値はその属性の値になります。例えば、 getattr(x, 'foobar') は x.foobar と等価です。指名された属性が存在しない場合、 default が与えられていればそれが返され、そうでない場合には AttributeError が送出されます。

getattr() ドキュメント

ドキュメントに書いてある通りgetattrを使うことで、辞書型のようにプロパティ名を文字列でも指定できます。
なので、先程の関数を以下のように書き換えることでクエリの内容を動的に変更することに成功しました!

    def get_related_data(model, parent_data, child_table_property):
       # getattrを使用して引数でモデルと属性名を渡すようにした
        child_table_value = getattr(model, child_table_property) 
        q = db.query(model.id, model.description).filter(
            model.status != 9,
            parent_data["id"] == child_table_value
        )
        return q.all()

  db_buildings = get_related_data(BuildingModel, account, "account_id")

こうすることでchild_table_valueBuildingModel["account_id"]となり、値を取得できるようになりました。
加えて、渡す値を引数で変えることによってデータのプロパティへ動的にアクセスすることができるようになりました