sqlalchemy Error creating backref on relationship

Question:

I have two very simple models. In my Post model there are supposed to be two relationships into the User table. One is for the owner of the post and one is for the last editor of the post. They can be different values, but both refer to the same User table.

My models are set up like this

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)

When I attempt to create these models though, I get the following error

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

How do I correct this so that I can maintain both forgeign keys in the Post model?

Asked By: NewGuy

||

Answers:

The error is telling you that you’ve used post as a name more then once for your backrefs, all you need to do is give the backref’s unique names. Here’s a complete example– I’ve added a id primary key to the Post class, and also some __repr__s so we get some readable output.

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>

Now when you query a user, you can ask that object user.owner_posts or user.editor_posts.

Answered By: Doobeh

In general it’s a naming Problem of the backref.

Since 1:n relationships are sometimes a bit confusing, I set the relationship attribute
always on the singular site, to avoid confusion.

then the backref name is always singular. and the relationship attribute is always in the Class where the foreignkey is referencing to.

Now to my suggestion for the fixed code:

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')

Now you can get all the owned posts of a User with User.owned_posts and all owners of a post with Post.owner. Same with the last_edited attribute.

For additional info you could read the docs how to set up relationships

Answered By: muthan
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.