4

私は、作成されるすべてのモデルに対して一般的なカスタマイズを必要とするプロジェクトに取り組んでいます。私がこれまで行ってきた作業のほとんどは、モデルの継承によるものです。より良いアイデアを提供するための私のコードブロックは次のとおりです。

app.core.dba.mixins:

class AuditExtension(MapperExtension):
    """
    AuditExtension enforces the audit column values, and ensures any interaction with
    SQLAlchemy cannot override the values
    """

    def before_insert(self, mapper, connection, instance):
        instance.created_dt = datetime.utcnow()
        instance.created_by = audit_session_user()

        instance.updated_dt = datetime.utcnow()
        instance.updated_by = audit_session_user()

    def before_update(self, mapper, connection, instance):
        # Never update the created columns
        instance.created_dt = instance.created_dt
        instance.created_by = instance.created_by

        instance.updated_dt = datetime.utcnow()
        instance.updated_by = audit_session_user()


class AuditColumns(object):

    """ Generate the column schema for simple table level auditing. """
    created_dt = Column(DateTime,
                        default=datetime.utcnow(),
                        nullable=False)
    created_by = Column(String(64),
                        #ForeignKey('operators.username', ondelete="RESTRICT"),
                        nullable=False)

    updated_dt = Column(DateTime,
                        default=datetime.utcnow(),
                        nullable=False,
                        onupdate=datetime.utcnow())

    updated_by = Column(String(64),
                        #ForeignKey('operators.username', ondelete="RESTRICT"),
                        nullable=False)

    __mapper_args__ = {
        'extension': AuditExtension()}

私のモデルは、AuditColumns を継承します。

class ObjectTypes(Base, AuditColumns):
    __tablename__ = 'object_types'

    id = Column(BigInteger, primary_key=True)
    name = Column(String, nullable=False, unique=True)

    def __repr__(self):
        return self.name

私の問題は; 操作がフラスコアプリとSQLAlchemyに含まれている限り、監査データを強制するための私のソリューションは機能します-これは、データベースにアクセスして値を更新することを妨げません。

したがって、AuditColumns を継承する各モデルにトリガーを実装する必要があります。この投稿Sqlalchemy mixins / and event listener - を見つけました-これは before_insert/update (以前は機能していました) のメソッドについて説明していますが、「after_create」については説明していません。

ここで、これを mixins ファイル コードに追加しました (上記の監査コードの直後:

trig_ddl = DDL("""
            CREATE TRIGGER tr_audit_columns BEFORE INSERT OR UPDATE
            ON test_table
            FOR EACH ROW EXECUTE PROCEDURE
            ss_test();
        """)

event.listen(AuditColumns, 'after_create', trig_ddl)

ただし、テストケースを実行すると:

Base.metadata.drop_all(db.get_engine(app))
Base.metadata.create_all(db.get_engine(app))

次のエラーが表示されます。

File "D:\Devel\flask-projects\sc2\app\core\dba\mixins.py", line 59, in <module>
    event.listen(AuditColumns, 'after_create', trig_ddl)
  File "D:\Devel\flask-projects\env\lib\site-packages\sqlalchemy\event.py", line 43, in listen
    (identifier, target))
sqlalchemy.exc.InvalidRequestError: No such event 'after_create' for target '<class 'app.core.dba.mixins.AuditColumns'>'

これはまだテーブルではないためだと思います。しかし、このタイプのコマンドを実行するテーブル作成のイベントリスナーをグローバルに定義するにはどうすればよいでしょうか?

trig_ddl を動的にする必要があることはわかっています (これはそれほど難しくないと思いますが、少なくともこのグローバル要素を理解する必要があります)。

基本的に、これらの監査列に明確に関連付けられている場合、このイベントを各モデルに手動で書き込む必要はありません。

正しい方向へのプッシュは素晴らしいでしょう。

4

1 に答える 1