¿Cómo establecer una relación de uno a muchos y de uno a uno al mismo tiempo en Flask-SQLAlchemy?

Estoy intentando crear una relación uno a uno y uno a muchos al mismo tiempo en Flask-SQLAlchemy. Quiero lograr esto:

“Un grupo tiene muchos miembros y un administrador”.

Aquí esta lo que hice:

class Group(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(140), index=True, unique=True) description = db.Column(db.Text) created_at = db.Column(db.DateTime, server_default=db.func.now()) members = db.relationship('User', backref='group') admin = db.relationship('User', backref='admin_group', uselist=False) def __repr__(self): return '' % (self.name) class User(db.Model): id = db.Column(db.Integer, primary_key=True) group_id = db.Column(db.Integer, db.ForeignKey('group.id')) admin_group_id = db.Column(db.Integer, db.ForeignKey('group.id')) created_at = db.Column(db.DateTime, server_default=db.func.now()) 

Sin embargo tengo un error:

sqlalchemy.exc.AmbiguousForeignKeysError: No se pudo determinar la condición de unión entre las tablas padre / hijo en la relación Group.members: hay varias rutas de clave externa que vinculan las tablas. Especifique el argumento ‘foreign_keys’, que proporciona una lista de aquellas columnas que deben contarse como que contienen una referencia de clave externa a la tabla principal.

¿Alguien sabe cómo hacerlo correctamente?

El problema que está surgiendo proviene del hecho de que ha definido dos enlaces entre sus clases: un usuario tiene un group_id (que es una clave externa) y un grupo tiene un administrador (que también está definido por una clave externa) . Si elimina la clave externa del campo de administración, la conexión ya no es ambigua y la relación funciona. Esta es mi solución a su problema (haciendo el enlace uno a uno):

 from app import db,app class Group(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(140), index=True, unique=True) description = db.Column(db.Text) created_at = db.Column(db.DateTime, server_default=db.func.now()) admin_id = db.Column(db.Integer) #, db.ForeignKey('user.id')) members = db.relationship('User', backref='group') def admin(self): return User.query.filter_by(id=self.admin_id).first() class User(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True) group_id = db.Column(db.Integer, db.ForeignKey('group.id')) created_at = db.Column(db.DateTime, server_default=db.func.now()) 

El único inconveniente de esto es que el objeto de grupo no tiene un objeto de miembro de admin limpio que solo pueda usar; debe llamar a la función group.admin() para recuperar al administrador. Sin embargo, el grupo puede tener muchos miembros, pero solo uno de ellos puede ser el administrador. Obviamente, no hay una comprobación de nivel de base de datos para garantizar que el administrador sea realmente un miembro del grupo, pero podría agregar esa comprobación en una función de establecimiento, tal vez algo como:

 # setter method def admin(self, user): if user.group_id == self.id: self.admin_id = user.id # getter method def admin(self): return User.query.filter_by(id=self.admin_id).first() 

La solución es especificar el argumento foreign_keys en todas las relationship s:

 class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) group_id = Column(Integer, ForeignKey('groups.id')) admin_group_id = Column(Integer, ForeignKey('groups.id')) class Group(Base): __tablename__ = 'groups' id = Column(Integer, primary_key=True) members = relationship('User', backref='group', foreign_keys=[User.group_id]) admin = relationship('User', backref='admin_group', uselist=False, foreign_keys=[User.admin_group_id]) 

Quizás considere la relación de administrador en la otra dirección para implementar “un grupo tiene muchos miembros y un administrador”:

 class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) group_id = Column(Integer, ForeignKey('groups.id')) group = relationship('Group', foreign_keys=[group_id], back_populates='members') class Group(Base): __tablename__ = 'groups' id = Column(Integer, primary_key=True) members = relationship('User', foreign_keys=[User.group_id], back_populates='group') admin_user_id = Column(Integer, ForeignKey('users.id')) admin = relationship('User', foreign_keys=[admin_user_id], post_update=True) 

Ver nota en post_update en la documentación . Es necesario cuando dos modelos son mutuamente dependientes, haciendo referencia entre sí.

Ok, encontré una solución para este problema finalmente. La relación muchos a muchos puede coexistir con una relación uno a muchos entre las mismas dos tablas al mismo tiempo.

Aquí está el código:

 groups_admins = db.Table('groups_admins', db.Column('user_id', db.Integer, db.ForeignKey('user.id')), db.Column('group_id', db.Integer, db.ForeignKey('group.id')) ) class Group(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(140), index=True, unique=True) description = db.Column(db.Text) created_at = db.Column(db.DateTime, server_default=db.func.now()) members = db.relationship('User', backref='group') admins = db.relationship('User', secondary=groups_admins, backref=db.backref('mod_groups', lazy='dynamic'), lazy='dynamic') def __repr__(self): return '' % (self.name) class User(db.Model): id = db.Column(db.Integer, primary_key=True) group_id = db.Column(db.Integer, db.ForeignKey('group.id')) created_at = db.Column(db.DateTime, server_default=db.func.now()) 

Todavía quiero que alguien me diga cómo establecer una relación de uno a varios y de uno a uno al mismo tiempo, así que dejo mi respuesta aquí y no la aceptaré para siempre.