1

我最近遇到了这个问题,我已经坚持了几天了。基本上,这里感兴趣的两个表是“Activity”和“Goal”,其中 Activity 是 Goal 的单亲;一个目标只有一个父级,一个活动有一个或多个目标子级:

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

这里有一些非常规的用法,并且您的 _ init _s 中存在一些不必要的循环,因为 sqlalchemy 的 ORM 已经为您做了什么,这可能会导致问题。

ORM 已经能够让您的 Activity 和 Goal 实例在逻辑上链接在一起,而无需您在__init__方法中强制执行此操作。

尝试以下操作:

  1. 不要Goal.__init__将活动作为参数。顺便说一句,您甚至不必为__init__使用 sqlalchemy 的声明性基础的模型类编写方法;你可以,但只有当你真的需要完成一些额外的事情时才有必要。那么您的新 Goal 实例将如何链接到它的一个 Activity 实例呢?Activity.goals 关系及其 backref 会为您处理这些问题。

  2. 顺便说一句,由于每个 Goal 仅与单个 Activity 相关联,因此您应该更改 backref 的形式。您的 currentbackref='activity'将生成与每个目标相关联的活动集合(在这种情况下,默认情况下为列表)。给定您的模型描述,您想要的是backref=backref('activity', uselist=False). 这将导致对 somegoal.activity 的引用返回单个对象(在 sqlalchemy 术语中,一个“标量”),而不是列表。

  3. 没有Activity.__init__添加目标;只需在调用 Activity() 以创建您的活动实例的代码部分中执行此操作。一般来说,建立像你在这里的初始化链是不必要的复杂化,其中在内部实例化一个事物会导致另一个事物的实例化。最好将该活动提升到一个级别并将其放入想要执行这些操作的主程序代码中,并让每个实例化都用自己的代码行明确。比把它藏在__init__s 里面要好得多。这也更加pythonic(显式优于隐式,使用更少的“魔法”)。

  4. Notice that in your Activity.add_goal() method nothing is actually done with the return value. Don't write methods whose return value is thrown away, or where some other method (in this case a constructor) is called only for side effects. And points 1 and 3 above could be summarized as "don't write constructors with side effects".

My point here is not to be dogmatic; I hope you can see that each of these suggestions is for the purpose of creating code that is clearer and accomplishes the desired things with less complication, and which turns out easier to read, to follow, and to debug.

至于对错误回溯中发生的事情的实际分析:我们需要一个实际工作的代码示例才能真正做到这一点,但我的猜测是 sqlalchemy 会按照您的要求创建您的 Activity 对象,并尝试遵循Activity该类映射器relationship创建从新活动到目标的前向引用,以及从目标到活动的反向引用。__init__但是在此过程中,您试图让您的 s 做一些工作的方式让您感到困惑。我认为如果你解开并简化一点,你很可能会得到你想要的。

Final note: If you have not gone through sqlalchemy's ORM tutorial section by section and followed every bit of it directly, that is strongly recommended. Along the way (and in following some of the background references on the side) you would find examples showing situations like the one you want to model here, and other very similar ones, and you'd see the most common (and simplest/clearest/easiest) ways of setting them up.

于 2012-06-25T01:31:38.627 回答