SQLAlchemyのバージョニングはクラスのインポート順序を考慮します

| | | | | | | | | |

ここでガイドに従っていました:

http ://www.sqlalchemy.org/docs/orm/examples.html?highlight = versioning#versioned-objects

問題が発生しました。次のように関係を定義しました:

  generic_ticker = Relation( "MyClass"、backref = backref( "stuffs")) 

文字列を使用して、 「モデルモジュールのインポート順序は気にしないでください。これはすべて正常に機能しますが、バージョン管理メタを使用すると、次のエラーが発生します。

sqlalchemy.exc.InvalidRequestError:マッパーを初期化するときMapper | MyClass | stuffs、式 "Trader"が名前を見つけられませんでした( "name" MyClass "が定義されていません")。これがクラス名の場合は、両方の依存クラスが定義された後で、このrelationship()をクラスに追加することを検討してください。 。

エラーを追跡しました:

ファイル"/home/nick/workspace/gm3/gm3/lib/history_meta.py"、line 90、__ init__ mapper = class_mapper(cls)ファイル "/home/nick/venv/tg2env/lib/python2.6/site-packages/sqlalchemy/orm/util.py"、622行、class_mapper mapper = mapper.compile () 

  class VersionedMeta(DeclarativeMeta):def __init __(cls、cl assname、bases、dict_):DeclarativeMeta .__ init __(cls、classname、bases、dict_)try:mapper = class_mapper(cls)_history_mapper(mapper)ただし、UnmappedClassError:pass  

問題を修正しましたtryを置くことによって:ラムダにあるものを除いて、すべてのインポートが行われた後にそれらをすべて実行します。これは機能しますが、少しごちゃごちゃしているようです。これを修正する方法についてのアイデアは、より良い方法ですか?

ありがとうございます!

更新

問題は実際には輸入注文に関するものではありません。バージョン管理の例は、マッパーが各バージョン管理されたクラスのcostructorでコンパイルする必要があるように設計されています。また、関連するクラスがまだ定義されていない場合、コンパイルは失敗します。循環関係の場合、マップされたクラスの定義順序を変更して機能させる方法はありません。

更新2

上記の更新の状態(ここで他の人の投稿を編集できるとは知りませんでした:))これは循環参照が原因である可能性があります。その場合、誰かが私のハックが役立つと思うかもしれません(私はターボギアでそれを使用しています)(VersionedMetaを置き換え、history_metaにグローバルなcreate_mappersを追加します)

  create_mappers = [] class VersionedMeta(DeclarativeMeta) :def __init __(cls、classname、bases、dict_):DeclarativeMeta .__ init __(cls、classname、bases、dict_)#クラッシュしていたので、このコードを追加しました。それ以外の場合はdef make_mapper():try:mapper = class_mapper(cls)_history_mapper (mapper)UnmappedClassErrorを除く:pass create_mappers.append(lambda:make_mapper()) 

次に、モデルで次のようなことを実行できます__init__。py

 #ここにモデルモジュールをインポートします。frommyproj.lib.history_meta import create_mappers from myproj.model.misc import * from myproj.model.actor import * from myproj.model.stuff1 import * from myproj.model.instrument import * from myproj.model.stuff import * #setup the history [func()for func in create_mappers]  

このようにして、マッパーのみを作成しますすべてのクラスが定義された後。

更新3少し無関係ですが、状況によっては重複する主キーエラーが発生しました(一度に同じオブジェクトに2つの変更をコミットする) 。私の回避策は、新しいプライマリ自動インクリメントキーを追加することでした。もちろん、mysqlでは1つしか持てないので、履歴テーブルの作成に使用された既存のもののプライマリキーを解除する必要がありました。コード全体(hist_idと外部キー制約の削除を含む)を確認してください:< / p>

 """公式のsqlalchemyから盗まれた"""from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.orm import mapper、class_mapper、attributes、object_mapper from sqlalchemy.orm.exc import UnmappedClassError、 UnmappedColumnError from sqlalchemy import Table、Column、ForeignKeyConstraint、Integer from sqlalchemy.orm.interfaces import SessionExtension from sqlalchemy.orm.properties import RelationshipProperty from sqlalchemy.types import DateTime import datetime from sqlalchemy.orm.session import Session def col_references_table(col、table) :col.foreign_keysのfkの場合:if fk.references(table):return True return False def def _history_mapper(local_mapper):cls = local_mapper.class_#「active_history」フラグを#列にマップされたlocal_mapper.iterate_propertiesのpropの古いバージョンの情報が常にロードされるように(現在はすべての属性に設定されます)、getattr(local_mapper.class_、prop.key).impl.active_history = True super_mapper = local_mapper.inherits super_history_mapper = getattr(cls、 "__history_mapper__"、None)polymorphic_on=なしsuper_fks= []そうでない場合super_mapperまたはlocal_mapper.local_tableがsuper_mapper.local_tableではない:cols = [] for column in local_mapper.local_table.c:if column.name == "version":continue col = column.copy()col.unique = False #don "col.autoincrement:col.autoincrement = Falseの場合、通常のデータベースからコンテンツを自動インクリメントしません。複合キーifcol.primary_key:col.primary_key = False if super_mapper and col_references_table(column、super_mapper.local_table):super_fks.append((col.key、list(super_history_mapper.base_mapper.local_table.primary_key)[0]))cols。列がlocal_mapper.polymorphiの場合はappend(col) c_on:polymorphic_on = col #if super_mapper:#super_fks.append(( "version"、super_history_mapper.base_mapper.local_table.c.version))cols.append(Column( "hist_id"、Integer、primary_key = True、autoincrement = True) )cols.append(Column( "version"、Integer))cols.append(Column( "changed"、DateTime、default = datetime.datetime.now))if super_fks:cols.append(ForeignKeyConstraint(* zip(* super_fks) ))table = Table(local_mapper.local_table.name + "_history"、local_mapper.local_table.metadata、* cols、mysql_engine = "InnoDB")else:#単一のテーブルの継承。 #追加された可能性のある追加の列を取得し、履歴テーブルに追加します。 local_mapper.local_table.cの列の場合:column.keyがsuper_history_mapper.local_table.cにない場合:col = column.copy()super_history_mapper.local_table.append_column(col)table = None if super_history_mapper:bases =(super_history_mapper.class_、) else:bases = local_mapper.base_mapper.class _.__ bases__ versioned_cls = type .__ new __(type、 "%sHistory"%cls .__ name__、bases、{})m = mapper(versioned_cls、table、inherits = super_history_mapper、polymorphic_on = polymorphic_ = local_mapper.polymorphic_identity)cls .__ history_mapper __ = m if not super_history_mapper:cls.version = Column( "version"、Integer、default = 1、nullable = False)create_mappers = [] class VersionedMeta(DeclarativeMeta):def __init __(cls、classname 、bases、dict _):DeclarativeMeta .__ init __(cls、classname、bases、dict_)#クラッシュしていたので、このコードを追加しました。それ以外の場合はdef make_mapper():try:mapper = class_mapper(cls)_history_mapper(mapper) create_mappers.append(lambda: make_mapper())def versioned_objects(iter):for obj in iter:if hasattr(obj、 "__history_mapper__"):yield obj def create_version(obj、session、deleted = False):obj_mapper = object_mapper(obj)history_mapper = obj .__ history_mapper__ history_cls = history_mapper.class_ obj_state = attributes.instance_state(obj)attr = {} obj_changed = omの場合はFalse、zipのhm(obj_mapper.iterate_to_root()、history_mapper.iterate_to_root()):hm.singleの場合:hmのhist_colを続行.local_table.c:if hist_col.key=="version"またはhist_col.key=="changed"またはhist_col.key=="hist_id":続行obj_col = om.local_table.c [hist_col.key]#値を取得#マップされた列に関連するMapperPropertyに基づく#属性の。これにより、マップされた列のキー名とは異なるキー名を持つMapperProperties#を使用できるようになります。 try:prop = obj_mapper.get_property_by_column(obj_col)ただし、UnmappedColumnError:#単一テーブル継承の場合、マップされたテーブルにサブクラスのみを対象とした#列が存在する可能性があります。 #基本クラスのサブクラス列の「マップされていない」ステータスは、sqla0.5.2以降の宣言モジュールの機能です。 #期限切れのオブジェクト属性を続行し、遅延列も#dictに含まれていない可能性があります。 getattr()を使用して、何があっても強制的にロードします。 prop.keyがobj_state.dictにない場合:getattr(obj、prop.key)a、u、d = attributes.get_history(obj、prop.key)if d:attr [hist_col.key] = d [0] obj_changed = True elif u:attr [hist_col.key] = u [0] else:#属性に値がない場合。 attr [hist_col.key] = a [0] obj_changed = obj_changedでない場合はTrue:#変更されていませんが、関係があります。 OK#obj_mapper.iterate_propertiesのpropについてもチェックします:if isinstance(prop、RelationshipProperty)and attributes.get_history(obj、prop.key).has_changes():obj_changed = True break if not obj_changed and not delete:return attr [" version "] = obj.version hist = history_cls()for key、value in attr.iteritems():setattr(hist、key、value)obj.version + = 1 session.add(hist)class VersionedListener(SessionExtension):def before_flush(self、session、flush_context、instances):versioned_objects(session.dirty)のobjの場合:versioned_objects(session.deleted)のobjのcreate_version(obj、session):create_version(obj、session、deleted = True) 

Shop

Learn programming in R: courses

$

Best Python online courses for 2022

$

Best laptop for Fortnite

$

Best laptop for Excel

$

Best laptop for Solidworks

$

Best laptop for Roblox

$

Best computer for crypto mining

$

Best laptop for Sims 4

$

Latest questions

NUMPYNUMPY

psycopg2: insert multiple rows with one query

12 answers

NUMPYNUMPY

How to convert Nonetype to int or string?

12 answers

NUMPYNUMPY

How to specify multiple return types using type-hints

12 answers

NUMPYNUMPY

Javascript Error: IPython is not defined in JupyterLab

12 answers

News


Wiki

Python OpenCV | cv2.putText () method

numpy.arctan2 () in Python

Python | os.path.realpath () method

Python OpenCV | cv2.circle () method

Python OpenCV cv2.cvtColor () method

Python - Move item to the end of the list

time.perf_counter () function in Python

Check if one list is a subset of another in Python

Python os.path.join () method