sqlAlchemy: Could not determine join condition between parent/child tables - python

I've been trying to solve this for hours now, I've read through a few stack overflows, the official SQL alchemy documentation and watched youtube tutorials and it's all the same code as mine.
I have 3 tables - Users and Roles, and user_role which joins them.
I keep getting the following error:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship
Users.role - there are no foreign keys linking these tables via secondary table 'user_role'. Ensure that referencing
columns are associated with a ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' and 'secondaryjoin'
expressions.
My code:
engine = create_engine('***',
connect_args={'options': '-csearch_path={}'.format('flaskProject')})
Base = declarative_base(engine)
Base.query = db.session.query_property()
user_role = Table('user_role', Base.metadata,
Column('userId', Integer, ForeignKey("Users.id")),
Column('roleId', Integer, ForeignKey("Roles.id")))
class Users(UserMixin, Base):
__tablename__ = 'users'
__table_args__ = {'schema': 'flaskProject'}
id = Column(Integer, primary_key=True)
username = Column(String)
email = Column(String)
password_hash = Column(String)
role = relationship("Roles", secondary = user_role)
class Roles(Base):
__table_args__ = {'schema': 'flaskProject'}
__tablename__ = 'roles'
id = Column(Integer, primary_key=True)
name = Column(String)

I found out the issues with my secondary table - I was missing the schema in the table definition, had to add the schema to the foreign keys, and column names are case-sensitive
user_role = Table('user_role', Base.metadata,
Column('userid', Integer, ForeignKey('flaskProject.users.id')),
Column('roleid', Integer, ForeignKey('flaskProject.roles.id'))
,schema='flaskProject')

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

Making a one-to-one join to an inherited table via the primary key in SQLAlchemy

I have a MySQL table 'users' with the following fields
users
----
id: int (pk)
name: string
email: string
can_edit_records: bool
can_delete_records: bool
And I want to represent that as two separate models in SqlAlchemy ORM in models User and UserPermissions which are joined to each other so that you can access the permissions of a user via user.permissions.
Here's what I've got so far
class UserBase(Model):
__tablename__ = 'users'
base_id = Column(Integer, primary_key=True, name="id")
class User(UserBase):
__table_args__ = {'extend_existing': True}
id = Column(ForeignKey("users.id"), primary_key=True)
name = Column(String)
email = Column(String)
permissions = relationship("UserPermissions", primaryjoin="User.id==foreign(UserPermissions.id)")
class UserPermissions(UserBase):
__table_args__ = {'extend_existing': True}
id = Column(ForeignKey("users.id"), primary_key=True)
can_edit_records = Column(Boolean)
can_delete_records = Column(Boolean)
which throws
ArgumentError: Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition 'users.id = users.id' on relationship User.permissions. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation. To allow comparison operators other than '==', the relationship can be marked as viewonly=True.

SQLAlchemy: AmbiguousForeignKeysError double reference to parent

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)

How to query those Objects that are not joined (SQLAlchemy)

I have two models in SQLAlchemy, having a many-to-many relationship
team_user_table = Table('team_user', Base.metadata,
Column('user_id', Integer, ForeignKey('users.id')),
Column('team_id', Integer, ForeignKey('teams.id'))
)
class User(Base):
""" The SQLAlchemy declarative model class for a User object. """
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
class Team(Base):
""" The SQLAlchemy declarative model class for a Team object. """
__tablename__ = 'teams'
id = Column(Integer, primary_key=True)
name = Column(Text, unique=True)
members = relationship("User",
secondary=team_user_table,
backref="memberteams")
I would like to query those users that are not member of a specific team.
In SQL (for example):
SELECT u.id,u.name FROM users u WHERE u.id NOT IN (SELECT tu.user_id FROM team_user tu WHERE tu.team_id=?);
How can I do this in SQLAlchemy?
This does what you want:
team_id = 1
query = session.query(User.id).filter(~User.memberteams.any(Team.id == team_id))
This is the SQL it outputs (on MySQL):
SELECT users.id AS users_id, users.name AS users_name
FROM users
WHERE NOT (EXISTS (SELECT 1
FROM team_user, teams
WHERE users.id = team_user.user_id AND teams.id = team_user.team_id AND teams.id = %s))
This is not exactly what your query looks like now, but I think this is the way it should be done. Check the docs for this.
I tested this on MySQL. However, I think it should work on any other.
To have the exact query you are looking for, you might need to look into using subqueries in combination with the filter statement. I think you will need explicit reference to the team_user table in your code to make it work.
I believe this should work and it does not use subqueries.
session.query(User).join(team_users_table).filter(team_users_table.team_id != OTHER_TEAM)

sqlalchemy: referencing label()'d column in a filter or clauselement

I'm trying to perform a query that works across a many->many relation ship between bmarks and tags with a secondary table of bmarks_tags. The query involves several subqueries and I have a need to DISTINCT a column. I later want to join that to another table via the DISTINCT'd ids.
I've tried it a few ways and this seems closest:
tagid = alias(Tag.tid.distinct())
test = select([bmarks_tags.c.bmark_id],
from_obj=[bmarks_tags.join(DBSession.query(tagid.label('tagid'))),
bmarks_tags.c.tag_id == tagid])
return DBSession.execute(qry)
But I get an error:
⇝ AttributeError: '_UnaryExpression' object has no attribute 'named_with_column'
Does anyone know how I can perform the join across the bmarks_tags.tag_id and the result of the Tag.tid.distinct()?
Thanks
Schema:
# this is the secondary table that ties bmarks to tags
bmarks_tags = Table('bmark_tags', Base.metadata,
Column('bmark_id', Integer, ForeignKey('bmarks.bid'), primary_key=True),
Column('tag_id', Integer, ForeignKey('tags.tid'), primary_key=True)
)
class Tag(Base):
"""Bookmarks can have many many tags"""
__tablename__ = "tags"
tid = Column(Integer, autoincrement=True, primary_key=True)
name = Column(Unicode(255), unique=True)
Something like this should work:
t = DBSession.query(Tag.tid.distinct().label('tid')).subquery('t')
test = select([bmarks_tags.c.bmark_id], bmarks_tags.c.tag_id == t.c.tid)
return DBSession.execute(test)
It is hard to tell what you are trying to accomplish, but since you are using orm anyways (and there is not much reason anymore to go with bare selects in sa these days), you should probably start by establishing a many-to-many relation:
bmarks_tags = Table('bmark_tags', Base.metadata,
Column('bmark_id', Integer, ForeignKey('bmarks.bid'), primary_key=True),
Column('tag_id', Integer, ForeignKey('tags.tid'), primary_key=True)
)
class Tag(Base):
"""Bookmarks can have many many tags"""
__tablename__ = "tags"
tid = Column(Integer, primary_key=True)
name = Column(Unicode(255), unique=True)
class BMark(Base):
__tablename__ = 'bmarks'
bid = Column(Integer, primary_key=True)
tags = relation(Tag, secondary=bmarks_tags, backref="bmarks")
Then get your query and go from there:
query = DBSession.query(BMark).join(BMark.tags)
If not, give us the actual sql you are trying to make sqlalchemy emit.

Categories

Resources