1

私は最近この問題にぶつかり、数日間立ち往生しています。基本的に、ここで重要な 2 つのテーブルは「Activity」と「Goal」です。Activity は、Goal の単一の親です。ゴールにはちょうど 1 つの親があり、アクティビティには 1 つ以上のゴールの子があります。

class Activity(Base, Table):
    __tablename__ = 'activity'

    pk_id = Column(Integer, primary_key=True)
    name = Column(String)
    wave = Column(Integer)
    enabled = Column(Boolean)

    goals = relationship("Goal", backref='activity', single_parent=True, cascade="all,delete-orphan")
    activity_tags = relationship("ActivityTags", backref='tag_activity', cascade="all,delete-orphan")

class Goal(Base, Table):
    __tablename__ = 'goal'

    pk_id                 = Column(Integer, primary_key=True)
    activity_id           = Column(Integer, ForeignKey('activity.pk_id'))
    category_id           = Column(Integer, ForeignKey('category.pk_id'))

    name                  = Column(String)
    minutes_expected      = Column(Integer)
    date_last_invoked     = Column(Date)
    date_last_invalidated = Column(Date)
    enabled               = Column(Boolean)

    milestones = relationship("Milestone", backref='goal', single_parent=True, cascade="all,delete-orphan")

ここで、Activity オブジェクトを作成すると (1 つの Goal 子オブジェクトが作成されます)、次のエラーが発生します。

Traceback (most recent call last):
  File "unit.py", line 49, in wrapper
    r = func(*args, **kwargs)
  File "unit.py", line 158, in testActivityWaveOverdue
    a = Activity(c, 'Pull-Ups', wave)
  File "<string>", line 4, in __init__
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/state.py", line 98, in initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "/home/me/dev/coach/coach/activity.py", line 64, in __init__
    self.add_goal(category, 'maintenance')
  File "/home/me/dev/coach/coach/activity.py", line 190, in add_goal
    return Goal(self, category, name, minutes_expected)
  File "<string>", line 4, in __init__
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/state.py", line 98, in initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "/home/me/dev/coach/coach/goal.py", line 76, in __init__
    db.session.commit()
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 656, in commit
    self.transaction.commit()
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 314, in commit
    self._prepare_impl()
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 298, in _prepare_impl
    self.session.flush()
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1583, in flush
    self._flush(objects)
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1636, in _flush
    is_orphan = _state_mapper(state)._is_orphan(state) and state.has_identity
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1237, in _is_orphan
    state, key, optimistic=bool(state.key)):
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/instrumentation.py", line 331, in has_parent
    return self.get_impl(key).hasparent(state, optimistic=optimistic)
  File "/home/me/dev/coach/.venv/lib/python2.7/site-packages/sqlalchemy/orm/attributes.py", line 349, in hasparent
    assert self.trackparent, "This AttributeImpl is not configured to track parents."
AssertionError: This AttributeImpl is not configured to track parents.

アクティビティの初期化コードは次のとおりです。

def __init__(self, category, name, wave):
    self.name = name
    self.wave = wave
    self.enabled = True
    db.session.add(self)
    db.session.commit()

    self.add_goal(category, 'maintenance')

def add_goal(self, category, name, minutes_expected=30):
    return Goal(self, category, name, minutes_expected)

そしてゴールのそれ:

def __init__(self, activity, category, name, minutes_expected):
    self.activity_id            = activity.pk_id
    self.category_id            = category.pk_id
    self.name                   = name
    self.minutes_expected       = minutes_expected

    self.date_last_invoked      = START_OF_TIME
    self.date_last_invalidated  = START_OF_TIME
    self.enabled                = True

    db.session.add(self)
    db.session.commit()

上記に含めてほしいことが他にあれば教えてください - 投稿を必要以上に大きくしたくなかったので、問題に関連すると思われるものだけを含めました.

最後に、Activity と Goal が継承する "Table" スーパークラスは、単純に補助/ヘルパー メソッドを提供します。

4

1 に答える 1

2

ここには型にはまらない使い方があり、sqlalchemy の ORM が既に行っていることを考えると、不必要な_ init _s の循環性があり、それが問題を引き起こしている可能性があります。

__init__ORM には、Activity インスタンスと Goal インスタンスを論理的にリンクする機能がすでに備わっています。メソッドで命令的にリンクする必要はありません。

次のことを試してください。

  1. Goal.__init__パラメータとしてアクティビティを取得しないでください。ちなみに、 sqlalchemy の宣言ベースを利用するモデル クラスのメソッド記述する必要さえありません。__init__できますが、追加の何かを本当に達成する必要がある場合にのみ必要です。では、新しい Goal インスタンスをその 1 つの Activity インスタンスにどのようにリンクするのでしょうか? Activity.goals リレーションシップとその backref がそれを処理します。

  2. ちなみに、各ゴールは 1 つのアクティビティにしか関連付けられていないため、後方参照の形式を変更する必要があります。現在のbackref='activity'結果は、各目標にリンクされたアクティビティのコレクション (この場合、デフォルトではリスト) になります。モデルの説明を考えると、必要なものはbackref=backref('activity', uselist=False). その結果、リストではなく単一のオブジェクト (sqlalchemy 用語では「スカラー」) を返す somegoal.activity への参照が発生します。

  3. Activity.__init__目標を追加しないでください。Activity() を呼び出してアクティビティ インスタンスを作成するコードのセクションでそれを行うだけです。一般に、ここにあるような init のチェーンをセットアップすることは、不必要に複雑であり、内部で 1 つのインスタンスを作成すると、別のインスタンスが作成されます。そのアクティビティをレベルアップして、これらのことを実行したいメイン プログラム コードに配置し、それぞれのインスタンス化を独自のコード行で明確にすることをお勧めします。s の中に隠すよりもはるかに優れてい__init__ます。これはまた、より Pythonic です (明示的は暗黙的よりも優れており、「魔法」の使用は少なくなります)。

  4. メソッドでは、戻り値に対してActivity.add_goal()実際には何も行われていないことに注意してください。戻り値が破棄されるメソッドや、副作用のためだけに他のメソッド (この場合はコンストラクター) が呼び出されるメソッドを記述しないでください。そして、上記のポイント 1 と 3 は、「副作用のあるコンストラクターを作成しない」と要約できます。

ここで私が言いたいのは、独断的になることではありません。これらの提案のそれぞれが、より明確で複雑さの少ない目的のことを達成し、読みやすく、追跡しやすく、デバッグしやすいコードを作成することを目的としていることを理解していただければ幸いです。

エラー トレースバックで何が起こっているかの実際の分析について: 実際にそれを行うには、実際に動作するコード サンプルが必要ですが、私の推測では、sqlalchemy は要求どおりに Activity オブジェクトを作成し、Activityクラスに従おうとします。マッパーrelationshipは、新しいアクティビティからゴールへの前方参照と、ゴールからアクティビティへの後方参照の両方を作成します。しかし、途中で、__init__代わりに s にその作業の一部を実行させようとしている方法に混乱しています。もつれを解き、少し単純化すれば、あなたが望むものが得られる可能性が高いと思います.

最後の注意: sqlalchemy の ORM チュートリアルをセクションごとに読んでおらず、すべての部分に直接従っていない場合は、それを強くお勧めします。その過程で (そして、横にあるいくつかの背景参照をたどると)、ここでモデル化したい状況や他の非常に類似した状況を示す例を見つけることができ、最も一般的な (そして最も単純で明確な) ことがわかります。 /最も簡単な)それらを設定する方法。

于 2012-06-25T01:31:38.627 に答える