2014-11-01 7 views
5

Ho due modelli molto semplici. Nel mio modello Post ci dovrebbero essere due relazioni nella tabella User. Uno è per il proprietario del post e uno è per l'ultimo editor del post. Possono essere valori diversi, ma entrambi si riferiscono alla stessa tabella User.sqlalchemy Errore nella creazione della relazione di ritorno sulla relazione

I miei modelli sono impostati come questo

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) 

Quando tento di creare questi modelli, però, ottengo il seguente errore

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

Come faccio a correggere questo modo che io possa mantenere sia chiavi di forgeign nel modello Post?

risposta

9

L'errore ti sta dicendo che hai usato post come nome più di una volta per il tuo backrefs, tutto ciò che devi fare è dare i nomi univoci del backref. Ecco un esempio completo: ho aggiunto una chiave primaria ID alla classe Post e anche alcuni __repr__ in modo da ottenere un output leggibile.

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> 

Ora, quando si esegue una query di un utente, si può chiedere che oggetto user.owner_posts o user.editor_posts.

+0

questo fixies il problema di mappatura, ma lui non ha ancora un rapporto di lavoro. – muthan

+0

Beh, non funziona perché nella classe Post manca una chiave primaria, ma la relazione funziona correttamente. – Doobeh

+0

Ah ok non importa, è stato il mio fallimento. ho risolto la mia risposta ora, in modo da avere entrambe le soluzioni. bello sapere che funziona anche in questo modo. – muthan

2

In generale è un problema di denominazione del backref.

Poiché 1: le relazioni n sono a volte un po 'confuse, ho impostato l'attributo di relazione sempre sul sito singolare, per evitare confusione.

quindi il nome backrif è sempre singolare. e l'attributo di relazione è sempre nella classe a cui fa riferimento la chiave straniera.

Ora per il mio suggerimento per il codice fisso:

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

Ora è possibile ottenere tutti i messaggi di proprietà di un User con User.owned_posts e tutti i possessori di un post con Post.owner. Lo stesso con l'attributo last_edited.

Per ulteriori informazioni è possibile leggere le docs come impostare i rapporti

+0

"Inoltre, questo non dovrebbe comportare un rapporto di lavoro, poiché il tuo rapporto deve essere nella classe a cui stai riferendo e non in quelli con the Foreignkey "è sbagliato. Ho ampliato il mio esempio nell'altra risposta per dimostrarlo funzionante. – Doobeh