sqlalchemy 在关系上创建反向引用时出错

问题描述 投票:0回答:2

我有两个非常简单的模型。在我的

Post
模型中,
User
表中应该有两个关系。一个是帖子的所有者,一个是帖子的最后一位编辑。它们可以是不同的值,但都引用同一个
User
表。

我的模型是这样设置的

class Post(Base):
    last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
    last_editor = relationship('User', backref='posts', foreign_keys=[last_editor_id])
    owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
    owner = relationship('User', backref='posts', foreign_keys=[owner_id])

class User(Base):
    '''This represents a user on the site'''
    __tablename__ = 'users'
    id = Column(BigInteger, primary_key=True, unique=True)
    name = Column(BigInteger, nullable=False)

当我尝试创建这些模型时,出现以下错误

sqlalchemy.exc.ArgumentError: Error creating backref 'posts' on relationship 'Post.owner': property of that name exists on mapper 'Mapper|User|users'

如何纠正此问题,以便可以在

Post
模型中维护两个外键?

python sqlalchemy
2个回答
42
投票

错误告诉您,您已经多次使用

post
作为反向引用的名称,您所需要做的就是给出反向引用的唯一名称。这是一个完整的示例 - 我在 Post 类中添加了一个 id 主键,还有一些
__repr__
,以便我们获得一些可读的输出。

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, BigInteger, ForeignKey, Integer
from sqlalchemy.orm import relationship, sessionmaker

Base = declarative_base()
engine = create_engine('sqlite://') ## In Memory.
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()

class Post(Base):
    __tablename__ = 'post'
    id = Column(Integer, primary_key=True)
    last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
    last_editor = relationship('User', backref='editor_posts', foreign_keys=[last_editor_id])
    owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
    owner = relationship('User', backref='owner_posts', foreign_keys=[owner_id])

    def __repr__(self):
        return '<Post: {}>'.format(self.id)


class User(Base):
    '''This represents a user on the site'''
    __tablename__ = 'users'
    id = Column(BigInteger, primary_key=True, unique=True)
    name = Column(BigInteger, nullable=False)

    def __repr__(self):
        return '<User: {}>'.format(self.name)



Base.metadata.create_all(engine)

bob = User(name='Bob', id=1)
alice = User(name='Alice', id=2)
post = Post(owner=alice, last_editor=bob, id=1)

session.add(post)
session.commit()

bob = session.query(User).get(1)
print(bob)
# <User: Bob>
print(bob.editor_posts)
# [<Post: 1>]
print(bob.owner_posts)
# []

post = session.query(Post).get(1)
print(post.owner)
# <User: Alice>
print(post.last_editor)
# <User: Bob>

现在,当您查询用户时,您可以询问该对象

user.owner_posts
user.editor_posts


6
投票

总的来说,这是 backref 的命名问题。

由于1:n关系有时有点混乱,所以我设置了relationship属性 始终位于单一站点,以避免混淆。

那么 backref 名称始终是单数。并且关系属性始终位于外键引用的类中。

现在我对固定代码的建议:

class Post(Base):
    last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)

    owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)


class User(Base):
    '''This represents a user on the site'''
    __tablename__ = 'users'
    id = Column(BigInteger, primary_key=True, unique=True)
    name = Column(BigInteger, nullable=False)
    owned_posts = relationship('Post', backref='owner')
    edited_posts = relationship('Post', backref='last_editor')

现在您可以使用

User
获取
User.owned_posts
拥有的所有帖子,以及使用
Post.owner
获取帖子的所有所有者。与 last_edited 属性相同。

有关更多信息,您可以阅读文档如何建立关系

© www.soinside.com 2019 - 2024. All rights reserved.