SQLAlchemy: AmbiguousForeignKeysError double reference to parent - python

I have the following simplified code for Groups that can have subgroups with roles:
from sqlalchemy import (
CheckConstraint,
Column,
ForeignKey,
PrimaryKeyConstraint,
String,
create_engine,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
Base = declarative_base()
class Group(Base):
__tablename__ = 'groups'
name = Column(String, primary_key=True)
group_roles = relationship('GroupRole', back_populates='group')
class GroupRole(Base):
__tablename__ = 'group_roles'
name = Column(String, ForeignKey(Group.name))
group_name = Column(String, ForeignKey(Group.name))
role = Column(String, nullable=False)
__table_args__ = (
CheckConstraint('name != group_name'),
PrimaryKeyConstraint('name', 'group_name'),
)
group = relationship(Group,
foreign_keys=[name],
back_populates='group_roles')
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
session.add(Group(name='test'))
When I run this, I get sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Group.group_roles - there are multiple foreign key path
s linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
I have also tried adding the following below the GroupRole table in lieu of the corresponding line already defined in Group:
Group.group_roles = relationship('GroupRole', foreign_keys=[Column(String, ForeignKey(GroupRole.name))], back_populates='group')
But I get the same error, and it doesn't look like others have had to do this. What am I doing wrong? How can I get my GroupRole table to reference my Group table? I'm using SQLAlchemy 1.3 and python 3.7.

You can make the code work with only backref on the GroupRole.group relationship, such as:
class Group(Base):
__tablename__ = 'groups'
name = Column(String, primary_key=True)
class GroupRole(Base):
__tablename__ = 'group_roles'
name = Column(String, ForeignKey(Group.name))
group_name = Column(String, ForeignKey(Group.name))
role = Column(String, nullable=False)
__table_args__ = (
CheckConstraint('name != group_name'),
PrimaryKeyConstraint('name', 'group_name'),
)
group = relationship(Group,
foreign_keys=[name],
backref='group_roles')
Not sure this is what is intended though, but working:
g = Group(name='test', group_roles=[
GroupRole(group_name='testrole', name='test', role='testrole'),
GroupRole(group_name='testrole2', name='test', role='testrole2')
])
session.add(g)

Related

SqlAlchemy AmbiguousForeign Keys Error - foreign key attribute is specified [duplicate]

Am trying to setup a postgresql table that has two foreign keys that point to the same primary key in another table.
When I run the script I get the error
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Company.stakeholder - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
That is the exact error in the SQLAlchemy Documentation yet when I replicate what they have offered as a solution the error doesn't go away. What could I be doing wrong?
#The business case here is that a company can be a stakeholder in another company.
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
class Stakeholder(Base):
__tablename__ = 'stakeholder'
id = Column(Integer, primary_key=True)
company_id = Column(Integer, ForeignKey('company.id'), nullable=False)
stakeholder_id = Column(Integer, ForeignKey('company.id'), nullable=False)
company = relationship("Company", foreign_keys='company_id')
stakeholder = relationship("Company", foreign_keys='stakeholder_id')
I have seen similar questions here but some of the answers recommend one uses a primaryjoin yet in the documentation it states that you don't need the primaryjoin in this situation.
Tried removing quotes from the foreign_keys and making them a list. From official documentation on Relationship Configuration: Handling Multiple Join Paths
Changed in version 0.8: relationship() can resolve ambiguity between
foreign key targets on the basis of the foreign_keys argument alone;
the primaryjoin argument is no longer needed in this situation.
Self-contained code below works with sqlalchemy>=0.9:
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(u'sqlite:///:memory:', echo=True)
session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()
#The business case here is that a company can be a stakeholder in another company.
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
class Stakeholder(Base):
__tablename__ = 'stakeholder'
id = Column(Integer, primary_key=True)
company_id = Column(Integer, ForeignKey('company.id'), nullable=False)
stakeholder_id = Column(Integer, ForeignKey('company.id'), nullable=False)
company = relationship("Company", foreign_keys=[company_id])
stakeholder = relationship("Company", foreign_keys=[stakeholder_id])
Base.metadata.create_all(engine)
# simple query test
q1 = session.query(Company).all()
q2 = session.query(Stakeholder).all()
The latest documentation:
http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#handling-multiple-join-paths
The form of foreign_keys= in the documentation produces a NameError, not sure how it is expected to work when the class hasn't been created yet. With some hacking I was able to succeed with this:
company_id = Column(Integer, ForeignKey('company.id'), nullable=False)
company = relationship("Company", foreign_keys='Stakeholder.company_id')
stakeholder_id = Column(Integer, ForeignKey('company.id'), nullable=False)
stakeholder = relationship("Company",
foreign_keys='Stakeholder.stakeholder_id')
In other words:
… foreign_keys='CurrentClass.thing_id')

SqlAlchemy many to many relation with mm table and another relation using that mm table

in SqlAlchemy many to many relation with mm table you guys helped me with the many to many relation with a mm table.
Now I've got another table using the Category.uid_foreign for a relation. How to do that the right way?
I've tried it that way, with no succeed:
from sqlalchemy import (
create_engine,
Integer,
String,
ForeignKey,
)
from sqlalchemy.schema import (
Column,
)
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy.orm import Session
Base = declarative_base()
# This connection string is made up
engine = create_engine(
'postgresql+psycopg2://user:pw#/db',
echo=False)
class Category(Base):
__tablename__ = "categories"
uid = Column(
Integer,
primary_key=True,
autoincrement=True,
)
title = Column(String)
class Specification(Base):
__tablename__ = "specifications"
uid = Column(
Integer,
primary_key=True,
autoincrement=True,
)
title = Column(String)
class Product(Base):
__tablename__ = "products"
uid = Column(
Integer,
primary_key=True,
autoincrement=True,
)
title = Column(String)
class SysCategoryMM(Base):
__tablename__ = "categories_records"
uid_local = Column(Integer, ForeignKey("categories.uid"), primary_key=True)
uid_foreign = Column(Integer, foreign_keys=["Product.uid", "Specification.uid"], primary_key=True)
fieldname = Column(String)
product = relationship(
"Product",
backref="related_categories",
)
specification = relationship(
"Specification",
backref="related_specifications",
)
category = relationship(
"Category",
backref="related_products",
)
Results in:
sqlalchemy.exc.NoForeignKeysError: Can't find any foreign key relationships between 'categories_records' and 'products'.
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship SysCategoryMM.product - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
Could you elp me? Tried like hours...

Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression

Please could someone help me with this error? I have actually been really struggling to find solid, simple examples for SQLAlchemy. Whilst there are plenty of Model examples of there is not much examples of how to use these Models.
The Error:
sqlalchemy.exc.NoForeignKeysError:
Could not determine join condition between parent/child tables on relationship Species.sc_genus
- there are no foreign keys linking these tables.
Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
The Code
from sqlalchemy import Integer, Column, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation
Base = declarative_base()
class Genus(Base):
__tablename__ = 'genus'
id = Column(Integer, primary_key=True)
common_name = Column(String)
scientific_name = Column(String)
sc_sub_family = "sc_sub_family"
def __repr__(self):
return "<Genus(common_name='%s')>" % (self.scientific_name)
# Species is a child of Genus
class Species(Base):
__tablename__ = 'species'
id = Column(Integer, primary_key=True)
common_name = Column(String)
scientific_name = Column(String)
sc_genus = relation("Genus", backref="species")
def __repr__(self):
return "<Species(common_name='%s')>" % (self.scientific_name)
def addSpecies(session):
species = Species()
species.common_name = "House Cat"
species.scientific_name = "Felis catus"
genus = Genus()
genus.scientific_name = "Felis"
session.add(genus)
species.sc_genus = genus
session.add(species)
session.commit()
if __name__ == "__main__":
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
## A bunch of stuff to make the connection to the database work.
engine = create_engine('sqlite:///foos.db', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
addSpecies(session)
I needed to specify a foreign key for the relationship.
class Genus(Base):
__tablename__ = 'genus'
id = Column(Integer, primary_key=True)
scientific_name = Column(String)
# sc_sub_family = "sc_sub_family"
def __repr__(self):
return "<Genus(common_name='%s')>" % (self.scientific_name)
# Species is a child of Genus
class Species(Base):
__tablename__ = 'species'
id = Column(Integer, primary_key=True)
common_name = Column(String)
scientific_name = Column(String)
sc_genus = relation("Genus", backref="species")
sc_genus_id = Column(Integer, ForeignKey('genus.id'))
def __repr__(self):
return "<Species(common_name='%s')>" % (self.scientific_name)

issue creating sqlalchemy relationship/join with multiple foreign keys [duplicate]

I have this simple model of Author - Books and can't find a way to make firstName and lastName a composite key and use it in relation. Any ideas?
from sqlalchemy import create_engine, ForeignKey, Column, String, Integer
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
engine = create_engine('mssql://user:pass#library')
engine.echo = True
session = sessionmaker(engine)()
class Author(Base):
__tablename__ = 'authors'
firstName = Column(String(20), primary_key=True)
lastName = Column(String(20), primary_key=True)
books = relationship('Book', backref='author')
class Book(Base):
__tablename__ = 'books'
title = Column(String(20), primary_key=True)
author_firstName = Column(String(20), ForeignKey('authors.firstName'))
author_lastName = Column(String(20), ForeignKey('authors.lastName'))
The problem is that you have defined each of the dependent columns as foreign keys separately, when that's not really what you intend, you of course want a composite foreign key. Sqlalchemy is responding to this by saying (in a not very clear way), that it cannot guess which foreign key to use (firstName or lastName).
The solution, declaring a composite foreign key, is a tad clunky in declarative, but still fairly obvious:
class Book(Base):
__tablename__ = 'books'
title = Column(String(20), primary_key=True)
author_firstName = Column(String(20))
author_lastName = Column(String(20))
__table_args__ = (ForeignKeyConstraint([author_firstName, author_lastName],
[Author.firstName, Author.lastName]),
{})
The important thing here is that the ForeignKey definitions are gone from the individual columns, and a ForeignKeyConstraint is added to a __table_args__ class variable. With this, the relationship defined on Author.books works just right.

Unable to create self referencing foreign key in flask-sqlalchemy

I have a model Region and each Region can have sub-regions. Each sub-region has a field parent_id which is the id of its parent region. Here is how my model looks like
class Region(db.Model):
__tablename__ = 'regions'
__table_args__ = {'schema': 'schema_name'}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
parent_id = db.Column(db.Integer, db.ForeignKey('regions.id'))
parent = db.relationship('Region', primaryjoin=('Region.parent_id==Region.id'), backref='sub-regions')
created_at = db.Column(db.DateTime, default=db.func.now())
deleted_at = db.Column(db.DateTime)
Bu when i try to do db.create_all i get this error sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'regions.parent_id' could not find table 'regions' with which to generate a foreign key to target column 'id'
Why cant it find regions when i am specifying it in __tablename__? I am using flask-sqlalchemy version 1.0
EDIT --
i removed the line
__table_args__ = {'schema': 'schema_name'}
from my code and it works. Beats the hell out of me.
You must tell SQLAlchemy what the "remote side" of the relationship is, to distinguish between the current row and the row being joined to. The relevant explanation is located partway into this section of the documentation on relationships.
The relationship might look like this:
parent = db.relationship('Region', remote_side=id, backref='sub_regions')
This is an example demonstrating a self-referential relationship:
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.engine import create_engine
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(engine)
Base = declarative_base(engine)
session = Session()
class Region(Base):
__tablename__ = 'region'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent_id = Column(Integer, ForeignKey('region.id'), index=True)
parent = relationship(lambda: Region, remote_side=id, backref='sub_regions')
Base.metadata.create_all()
r1 = Region(name='United States of America')
r2 = Region(name='California', parent=r1)
session.add_all((r1, r2))
session.commit()
ca = session.query(Region).filter_by(name='California').first()
print ca.parent.name
There will be many lines of SQL output since echo is on, and the script will print 'United States of America' at the end.
I had the same issue with the schema name argument. What I changed to get it to work was to reference the table class directly in ForeignKey and relationships instead of using a string.
Example:
parent_id = Column(Integer, ForeignKey(Region.id), index=True)
parent = relationship(lambda: Region, remote_side=id, backref='sub_regions')
If you use a schema for any table, other tables that have foreign keys referencing those schema tables must provide the name of the schema. See the docs here
class Table(db.Model):
__tablename__ = 'table_1_name'
__table_args__ = {'schema': 'my_schema'}
id = Column('id', Integer, primary_key=True)
...
class AnotherTable(db.Model):
__tablename__ = 'table_2_name'
# Doesn't matter if this belongs to the same or different schema
# __table_args__ = {'schema': 'my_schema'}
id = Column('id', Integer, primary_key=True)
t1_id = Column(Integer, ForeignKey('my_schema.table_1_name.id'))
...
Works for both SQLAlchemy and Flask-SQLAlchemy. Hope this helps. :D
I only see slight differences from #davidism, but here's what works for me in straight SQLAlchemy.
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm import backref
class Region(Base):
__tablename__ = 'region'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('region.id'), index=True)
sub_regions = relationship('Region', backref=backref('parent', remote_side='Region.id'))
As he points out I'm guessing you wont need the imports, but should prefix them with db, so something like:
class Region(db.Model):
__tablename__ = 'region'
id = db.Column(db.Integer, primary_key=True)
parent_id = db.Column(db.Integer, db.ForeignKey('region.id'), index=True)
sub_regions = db.relationship('Region', backref=db.backref('parent', remote_side='Region.id'))

Categories

Resources