LoginSignup
0
1

More than 1 year has passed since last update.

SQLAlchemy modelにsetterとgetterを設定する方法

Last updated at Posted at 2022-07-03

まとめ(結論だけ知りたい人用)

下記リンク先のコードを参考にしてください。
modelにsetterとgetterを設定したコード

ただしprotectedなattributeにはアクセスしないという
共通認識が必要なコードとなっています。


コード内容を理解したい方は記事本文をどうぞ

概要

SQLAlchemyではmodelオブジェクトのattributeに直接値を代入することが可能です。
しかしmodelオブジェクトの内容変更がDBのレコード更新という動作に繋がりやすく、
単純なattributeの変更だと思っていた処理が、実はDB上のレコードの更新処理だった
というような動作内容への誤認識を招きやすくしています。

今回はattributeのsetterとgetterを実装することで、
attributeの読み出しは可能だが、代入はできないという性質を持つ
modelクラスの実装例を見ていきます。

定義するモデル

今回定義するモデルの内容は以下の通り

Field Type Null Key Default Extra
id varchar(26) NO PRI NULL
name varchar(255) NO NULL
email varchar(255) YES NULL

modelクラスのコード

以下の通り

from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import synonym
from sqlalchemy.schema import Column
from sqlalchemy.types import VARCHAR


Base = declarative_base()


class User(Base):
    __tablename__ = "user"
    _user_id = Column(VARCHAR(26), name="id", primary_key=True)
    _name = Column(VARCHAR(255), name="name", nullable=False)
    _email = Column(VARCHAR(255), name="email")

    def __init__(self, user_id=None, name=None, email=None) -> None:
        self._user_id = user_id
        self._name = name
        self._email = email

    @property
    def user_id(self):
        return self._user_id

    @property
    def name(self):
        return self._name

    @property
    def email(self):
        return self._email

    def update_name(self, name): # 抽象的なデータ操作メソッドを定義可能
        self._name = name

    # setterを書く例として定義。
    # 明示的に定義しなくてもAttributeErrorを発生してくれる。
    @name.setter
    def name(self, value):
        raise AttributeError

    name = synonym('_name', descriptor=name)
    user_id = synonym('_user_id', descriptor=user_id)
    email = synonym('_email', descriptor=email)

コード解説

  • Column
    全てprotectedな変数としてアクセスを禁じることを明示します。
    仕様的には参照可能ですが、言語上の共通認識としての普通はアクセスしないという思想に依存させます。

    ちなみにname manglingを利用すればいいと考えると思うのですが、
    後述のsynonymが利用できなくなるため利用できません。

  • @￰property
    getterを定義しています。

  • @￰[var].setter
    setterを定義しています。

  • synonym
    カラムのエイリアスを定義します。
    getter/setterを使うだけであれば、synonymを使わないでも問題ありません。
    しかし、以下のようにfilterを使う場合、attributeの参照で問題が発生します。

session.query(User).filter(User.name == "John").all()

  この時に生成されるクエリは以下の通り

SELECT user.id AS user_id, user.name AS user_name, user.email AS user_email 
FROM user 
WHERE false = 1

  明らかに意図していないクエリが発行されています。

  次にsynonymを追加した場合、

SELECT user.id AS user_id, user.name AS user_name, user.email AS user_email 
FROM user 
WHERE user.name = 'John'

  意図したとおりのクエリになっていることが分かります。

  ちなみにsynonymを使用しなかった場合、以下の方法で意図したクエリを発行できます。

session.query(User).filter(User._name == "John").all()

  しかし、protectedなattributeを指定するのはあまり良いコードとは言えないです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1