宣言ベースを使用して、SQLAlchemy でバックエンド アプリケーションを作成しています。ORM には約 15 個のテーブルが必要で、それぞれが SQLAlchemy のクラス オブジェクトにマップされます。これらのクラス オブジェクトはすべて同じように定義されているため、ファクトリ パターンを使用するとクラスをより簡潔に生成できると考えました。ただし、これらのクラスを定義するだけでなく、プロジェクト全体でインポートして使用できるように、一意の変数名に割り当てる必要があります。
(この質問が少し長い場合は申し訳ありませんが、問題をよりよく理解したので更新しました。)
非常に多くの列 (~1000) があるため、読みやすいように外部テキスト ファイルで名前と型を定義します。モデルを宣言する方法の 1 つを実行すると、次のようになります。
class Foo1(Base):
__tablename___ = 'foo1'
class Foo2(Base):
__tablename___ = 'foo2'
... etc
次に、外部テキスト ファイルの内容をループし、setattr()
各クラス定義で を使用して、列を追加できます。
これは問題ありませんが、約 15 のテーブルがあるため、繰り返しが多すぎるように感じます。その代わりに、クラスを動的に定義できるファクトリ関数を作成することに挑戦しました。
def orm_factory(class_name):
class NewClass(Base):
__tablename__ = class_name.lower()
NewClass.__name__ = class_name.upper()
return NewClass
ここでも、列をループして を使用できますsetattr()
。まとめてみるとこんな感じ。
for class_name in class_name_list:
ORMClass = orm_factory(class_name)
header_keyword_list = get_header_keyword_list(class_name)
define_columns(ORMClass, header_keyword_list)
Whereget_header_keyword_list
は列情報を取得し、代入をdefine_columns
実行します。setattr()
これを使用して実行するとBase.metadata.create_all()
、SQL スキーマが正常に生成されます。
しかし、これらのクラス定義を別のモデルにインポートしようとすると、次のようなエラーが発生します。
SAWarning: The classname 'NewClass' is already in the registry of this declarative base, mapped to <class 'ql_database_interface.IR_FLT_0'>
これは、昨日学んだことに基づいて完全に理にかなっていることがわかりました: Python クラス変数名 vs __name__。
ファクトリ関数でクラスジェネレーターとして使用することでこれに対処できますtype
(以下の2つの回答がそうです)。ただし、クラスがファクトリ関数で動的に構築されている間、その関数の出力が割り当てられる変数は静的であるため、これはクラスをインポートできるという問題を解決しません。辞書キーなどの動的なものであっても、別のモジュールからインポートするには、モジュールの名前空間に存在する必要があります。詳細については、私の回答を参照してください。