python - Flask-SQLAlchemy 数据模型关系问题

标签 python sqlalchemy flask flask-sqlalchemy

我正在尝试创建一个系统,其中 Person 是一个 WorkGroup 的一部分,并且每个 WorkGroup 包含许多 Person 秒。 Person 还包含以前匹配的 Person 的列表。以下是我目前所拥有的,我能够成功地将测试名称加载到其中。但是,在 findPartner 方法中,我尝试将新的 WorkGroup 分配给 Person workGroup 字段,如下所示:

self.workGroup = WorkGroup(self, newPartner)

我收到错误信息:

AttributeError: 'list' 对象没有属性 '_sa_instance_state'

这让我很困惑。 (我在 list 后包含了更完整的堆栈跟踪)。希望有人能指出错误(我只使用了大约 24 小时的 SQLAlchemy,所以我怀疑这是一些基本的错误)。

def sanitize(first, last):
    def san(nm):
        return " ".join([part.strip().capitalize().replace(",", "") 
                         for part in nm.split()])
    return san(first) + " " + san(last)


class Person(db.Model):
    __tablename__ = 'person'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    workGroup = db.relationship("WorkGroup", uselist=False)
    prevPairings_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    prevPairings = db.relationship('Person')

    def __init__(self, first, last):
        self.name = sanitize(first, last)
        self.workGroup = None
        self.prevPairings = []

    def __repr__(self):
        return self.name

    def findPartner(self, amongPeople):
        if self.workGroup:
            return # You already have a group
        candidates = [p for p in amongPeople if p is not self and 
                      p not in self.prevPairings and not p.workGroup]
        assert candidates
        newPartner = candidates[0] # Just choose the first available
        self.prevPairings.append(newPartner)
        newPartner.prevPairings.append(self)
        self.workGroup = WorkGroup(self, newPartner)
        newPartner.workGroup = self.workGroup

    def show(self):
        print "<Person %r %r %r %r>" % (self.id, self.name, self.prevPairings, self.workGroup)


class WorkGroup(db.Model):
    __tablename__ = 'workgroup'
    id = db.Column(db.Integer, primary_key=True)
    members_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    members = db.relationship('Person')

    def __init__(self, *partners):
        self.members = list(partners)

    def include(self, newMember):
        assert isinstance(newMember, Person)
        self.members.append(newMember)

    def group(self):
        return set(self.members)

    def __nonzero__(self):
        return bool(self.members)

    def __iter__(self):
        return iter(self.members)

    def clear(self):
        self.members = []

    def __str__(self): return str(self.members)
    def __repr__(self): return self.__str__()


############################################################

def generateNewPairings():
    all = Person.query.all()
    if max([len(p.prevPairings) for p in all]) > len(all) - 2:
        print "All Pairings Exhausted"
        for p in all:
            p.prevPairings = []
    for p in all:
        p.workGroup = None

    def pairUp(everyone):
        if len(everyone) % 2: # Odd number
            oddOne = random.choice(everyone)
            everyone.remove(oddOne)
            pairUp(everyone) # Recurse with even number
            random.choice(everyone).workGroup.include(oddOne)
            return
        for p in everyone: # Even number
            p.findPartner(everyone)

    pairUp(all)
    for p in all: print p.show()

这是完整的堆栈跟踪:

File "C:\Python27\lib\site-packages\flask\app.py", line 1701, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Python27\lib\site-packages\flask\app.py", line 1689, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "C:\Python27\lib\site-packages\flask\app.py", line 1687, in wsgi_app
response = self.full_dispatch_request()
File "C:\Python27\lib\site-packages\flask\app.py", line 1360, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Python27\lib\site-packages\flask\app.py", line 1358, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Python27\lib\site-packages\flask\app.py", line 1344, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 242, in generate_pairs
generateNewPairings()
File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 120, in generateNewPairings
pairUp(all)
File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 114, in pairUp
pairUp(everyone) # Recurse with even number
File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 118, in pairUp
p.findPartner(everyone)
File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 63, in findPartner
self.workGroup = WorkGroup(self, newPartner)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 238, in __set__
instance_dict(instance), value, None)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 736, in set
value = self.fire_replace_event(state, dict_, value, old, initiator)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 756, in fire_replace_event
value = fn(state, value, previous, initiator or self)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\unitofwork.py", line 89, in set_
sess._save_or_update_state(newvalue_state)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\session.py", line 1389, in _save_or_update_state
halt_on=self._contains_state):
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1928, in cascade_iterator
parent_dict, visited_states, halt_on))
File "C:\Python27\lib\site-packages\sqlalchemy\orm\properties.py", line 929, in cascade_iterator
get_all_pending(state, dict_)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 693, in get_all_pending
ret = [(instance_state(current), current)]
AttributeError: 'list' object has no attribute '_sa_instance_state'

最佳答案

我认为问题在于您在 PersonWorkGroup 类中都使用了 db.relationship()。将其设置在其中一个中,并使用 backref 关键字参数来定义另一侧的关系。

另一个问题是您将外键放在了错误的类中。因为你想要 many-to-one PersonWorkGroup的关系(多人在一个workgroup中),需要在Person类中定义workgroup_id FK并从 WorkGroup 中删除 members_id(和 members)。

在这些更改之后,我得到了 CircularDependencyError 异常,很可能是由于 prevPairings_id FK pointing to the same row .添加 post_update=TrueprevPairings 关系解决了这个问题。

所以这里是您需要为您的代码工作所做的所有修复:

class Person(db.Model):
    # ...
    workgroup_id = db.Column(db.Integer, db.ForeignKey('workgroup.id'))
    # No need to set uselist=False, it will be determined automatically by
    # type of relationship (many-to-one in this case).
    workGroup = db.relationship("WorkGroup", backref='members')
    # ...
    prevPairings = db.relationship('Person', post_update=True)
    # ...

class WorkGroup(db.Model):
    # ...
    # Removed members_id and members.
    # ...

关于python - Flask-SQLAlchemy 数据模型关系问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14143429/

相关文章:

python - 从 python 中的字符串运行 bash 命令列表

python - 是否可以从 psycopg2 或 sqlalchemy 为 PostgreSQL 发出 "VACUUM ANALYZE <tablename>"?

flask - 在flask-admin的创建/编辑表单中排除特定字段

python - 根据条件重新索引数据框的行

python - 在 seaborn 情节中更改图例标题大小的优雅方法?

python - Django {% blocktrans %} : How to handle pluralization inside a for loop?

python - 我可以在 ORM 事件回调中使用 SQLAlchemy 关系吗?总是没有

python - SQLAlchemy - 简单的选择语句

python - 在 OpenShift 中以 HTTPS 模式配置 python flask 应用程序

mongodb - 用两个条件检查记录的最简单方法是什么,如果不存在则插入,如果存在一个或多个,则全部更新