SQLAlchemy raises NoForeignKeyError with Many to One relations - python

I have three different classes I want to integrate with my database using SQLAlchemy. The relation I have is like Class2 object are main classes, each Class2 can have multiple Class1 objects which Class1 should have one to one relation with Class2. Each Class3 has two one to one relations with two different classes of type Class1.
import sqlalchemy as db
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Class1(Base):
__tablename__ = 'Class1s'
id = Column(Integer, primary_key=True)
owner = relationship("Class2", back_populates="Class1s")
crypto_type = Column(String)
balance = Column(Integer, primary_key=True, default=0)
class Class2(Base):
__tablename__ = 'Class2s'
id = Column(Integer, primary_key=True)
class Class3(Base):
__tablename__ = 'Class3'
id = Column(Integer, primary_key=True)
amount = Column(Integer)
source = relationship("Class1")
destination = relationship("Class1")
class Database:
def __init__(self):
self.engine = db.create_engine("sqlite:///database.db", echo=True)
session = sessionmaker()
session.configure(bind=self.engine)
Base.metadata.create_all(self.engine)
if __name__ == '__main__':
db = Database()
print('Database Initialized Successfully!')
user = Class2()
This raises the following error:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Class1.owner - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.

Please read Basic Relationship Patters
You indeed need to define how the tables underlying your classes are linked, which is done using the ForeignKey
class Class1(Base):
__tablename__ = 'Class1s'
id = Column(Integer, primary_key=True)
owner = relationship("Class2", back_populates="Class1s")
crypto_type = Column(String)
balance = Column(Integer, primary_key=True, default=0)
class Class2(Base):
__tablename__ = 'Class2s'
id = Column(Integer, primary_key=True)
class1_id = Column(ForeignKey('Class1s.id')) # <!-- NEW **
# Class1s = relationship("Class2", back_populates="owner")
In your case you will also need to be explicit when defining the relationship between the Class3 and Class1 (I guess, Transaction and TransactionLeg).
See Handling Multiple Join Paths for more information.
class Class3(Base):
__tablename__ = 'Class3'
id = Column(Integer, primary_key=True)
amount = Column(Integer)
source_id = Column(ForeignKey('Class2s.id')) # <!-- NEW **
destination_id = Column(ForeignKey('Class2s.id')) # <!-- NEW **
source = relationship("Class1", foreign_keys=[source_id]) # <!-- MODIFIED **
destination = relationship("Class1", foreign_keys=[destination_id]) # <!-- MODIFIED **
Please note that nothing above is tested and might contain typos.

Related

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)

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)

sqlalchemy: create two databases in the same file

I'm trying to create two separate databases in a single file using SQLAlchemy. Here's the code I have so far:
from sqlalchemy import create_engine, Column, String, Integer, inspect
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Table1(Base):
__tablename__ = 'table_1'
id = Column(Integer, primary_key=True)
name = Column(String)
class Table2(Base):
__tablename__ = 'table_2'
id = Column(Integer, primary_key=True)
name = Column(String)
engine1 = create_engine('sqlite:///db1.db')
engine2 = create_engine('sqlite:///db2.db')
Base.metadata.drop_all(bind=engine1)
Base.metadata.drop_all(bind=engine2)
Base.metadata.create_all(bind=engine1)
Base.metadata.create_all(bind=engine2)
print(inspect(engine1).get_table_names())
# ['table_1', 'table_2']
print(inspect(engine2).get_table_names())
# ['table_1', 'table_2']
I want to create only Table1 in db1 and only Table2 in db2; however, I'm getting both tables in both databases.
Is there anyway to resolve this or do I need to create the databases in two separate files.
Your problem isn't caused by trying to create the two databases in the same module. Rather, you are calling create_all() on the same metadata object that has mapped both of the tables. E.g.
print(Base.metadata.tables)
result:
dict_keys(['table_1', 'table_2'])
From the docs about MetaData.create_all():
This method will issue queries that first check for the existence of
each individual table, and if not found will issue the CREATE
statements...
Key point is it checks for the existence of each table. So here:
Base.metadata.create_all(bind=engine1)
Base.metadata.create_all(bind=engine2)
... it first checks for both tables in the db referenced by engine1, doesn't find them and creates them. Then, it checks for both tables in the db referenced by engine2, doesn't find them, and creates them.
There's a couple of options.
Have different Base objects (i.e. a distinct MetaData instance) for each database:
Base1 = declarative_base()
Base2 = declarative_base()
class Table1(Base1):
__tablename__ = 'table_1'
id = Column(Integer, primary_key=True)
name = Column(String)
class Table2(Base2):
__tablename__ = 'table_2'
id = Column(Integer, primary_key=True)
name = Column(String)
engine1 = create_engine('sqlite:///db1.db')
engine2 = create_engine('sqlite:///db2.db')
Base1.metadata.drop_all(bind=engine1)
Base2.metadata.drop_all(bind=engine2)
Base1.metadata.create_all(bind=engine1)
Base2.metadata.create_all(bind=engine2)
print(inspect(engine1).get_table_names())
# ['table_1']
print(inspect(engine2).get_table_names())
# ['table_2']
Or, selectively create the tables while binding to the desired engine:
Base = declarative_base()
class Table1(Base):
__tablename__ = 'table_1'
id = Column(Integer, primary_key=True)
name = Column(String)
class Table2(Base):
__tablename__ = 'table_2'
id = Column(Integer, primary_key=True)
name = Column(String)
engine1 = create_engine('sqlite:///db1.db')
engine2 = create_engine('sqlite:///db2.db')
Base.metadata.drop_all(bind=engine1)
Base.metadata.drop_all(bind=engine2)
Base.metadata.tables['table_1'].create(bind=engine1)
Base.metadata.tables['table_2'].create(bind=engine2)
print(inspect(engine1).get_table_names())
# ['table_1']
print(inspect(engine2).get_table_names())
# ['table_2']

Problems with Update and Table Inheritance with SQLAlchemy

I'm building an inheritance table schema like the following:
Spec Code
class Person(Base):
__tablename__ = 'people'
id = Column(Integer, primary_key=True)
discriminator = Column('type', String(50))
updated = Column(DateTime, server_default=func.now(), onupdate=func.now())
name = Column(String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
class Engineer(Person):
__mapper_args__ = {'polymorphic_identity': 'engineer'}
start_date = Column(DateTime)
class Manager(Person):
__mapper_args__ = {'polymorphic_identity': 'manager'}
start_date = Column(DateTime)
UPDATED (WORKING) CODE
import os
import sys
from sqlalchemy import Column, create_engine, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declarative_base
try:
os.remove('test.db')
except FileNotFoundError:
pass
engine = create_engine('sqlite:///test.db', echo=True)
Session = sessionmaker(engine)
Base = declarative_base()
class People(Base):
__tablename__ = 'people'
discriminator = Column('type', String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
id = Column(Integer, primary_key=True)
name = Column(String(50))
updated = Column(DateTime, server_default=func.now(), onupdate=func.now())
class Engineer(People):
__tablename__ = 'engineer'
__mapper_args__ = {'polymorphic_identity': 'engineer'}
id = Column(Integer, ForeignKey('people.id'), primary_key=True)
kind = Column(String(100), nullable=True)
Base.metadata.create_all(engine)
session = Session()
e = Engineer()
e.name = 'Mike'
session.add(e)
session.flush()
session.commit()
# works when updating the object
e.name = "Doug"
session.add(e)
session.commit()
# works using the base class for the query
count = session.query(People).filter(
People.name.is_('Doug')).update({People.name: 'James'})
# fails when using the derived class
count = session.query(Engineer).filter(
Engineer.name.is_('James')).update({Engineer.name: 'Mary'})
session.commit()
print("Count: {}".format(count))
Note: this is slightly modified example from sql docs
If I try to update the name for Engineer two things should happen.
update statement to the People table on column name
automatic trigger of update to the updated column on the People table
For now, i'd like to focus on number 1. Things like the example below (as also documented in the full code example) will result in invalid SQL
session.query(Engineer).filter(
Engineer.name.is_('James')).update({Engineer.name: 'Mary'})
I believe the above generates the following:
UPDATE engineer SET name=?, updated=CURRENT_TIMESTAMP FROM people WHERE people.name IS ?
Again, this is invalid. The statement is trying to update rows in incorrect table. name is in the base table.
I'm a little unclear about how inheritance tables should work but it seems like updates should work transparently with the derived object. Meaning, when I update Engineer.name querying against the Engineer object SQLAlchemy should know to update the People table. To complicate things a bit more, what happens if I try to update columns which exist in two tables
session.query(Engineer).filter(
Engineer.name.is_('James')).update({Engineer.name: 'Mary', Engineer.start_date: '1997-01-01'})
I suspect SQLAlchemy will not issue two update statements.

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