SqlAlchemy can't determine join condition - python

I have 2 tables defined:
class TCableSet(Base):
__tablename__ = 'tCableSet'
ixCableSet = Column(Integer, primary_key=True)
decCableSetOne = Column(Numeric(8, 2))
decCableSetTwo = Column(Numeric(8, 2))
decCableSetThree = Column(Numeric(8, 2))
class TStepVoltage(Base):
__tablename__ = 'tStepVoltage'
ixStepVoltage = Column(Integer, primary_key=True)
ixSubReport = Column(Integer, ForeignKey('tSubReport.ixSubReport'), nullable=False)
iVoltage = Column(Integer)
ixPhaseA = Column(Integer, ForeignKey('tCableSet.ixCableSet'), nullable=False)
ixPhaseB = Column(Integer, ForeignKey('tCableSet.ixCableSet'), nullable=False)
ixPhaseC = Column(Integer, ForeignKey('tCableSet.ixCableSet'), nullable=False)
sub_report = relationship('TSubReport',
backref=backref('step_voltage'))
I understand why I am getting this error but can't figure out a proper way (yet).
When the table gets saved, I store the values in the tCableSet table and then use the id as a foreign key in my tStepVoltage table. The problem I have is when I go to retrieve the data, I want to be able to get the values(tCableSet row) along with the rest of my tStepVoltage table via a relationship, however I'm not sure how to go about this since I don't have a field in my tCableSet that can directly be linked via relationship to my tStepVoltage. I basically just needed the tCableSet for normalization

Since you have more than one foreign key that points to the same table, you have to tell SQLAlchemy which foreign key to use.
For example:
sub_report_a = relationship('TSubReport',
backref=backref('step_voltage'),
foreign_keys=[ixPhaseA])

Related

django query to get data

I am into a very confusing situation where I have one to many relation and I want to query data like I want all parent table data but want to get only data from child tables which fulfill condition of site_id = 100.
class Policy(Base):
"""table containing details for Policies"""
__tablename__ = "UmbrellaPolicy"
id = Column(Integer, primary_key=True)
policy_id = Column(Integer, nullable=False, index=True)
user_defined_name = Column(String(255), nullable=True)
and child is like this
class Site(Base):
__tablename__ = "Site"
id = Column(Integer, primary_key=True)
policy_id = Column(Integer, ForeignKey("Policy.id"))
site_id = Column(String(32), nullable=False, index=True)
policy = relationship("Policy", backref="sites")
You should be able to filter join relations like this
parents = Policy.objects.filter(site__site_id=100)
You can find more info about the Django query API here but its generally of the form where you reference the relation with classname__columnname there are many other ways to filter/query that you can reference in the docs

Many to many relationship in sqlAlchemy with two foreign keys to the same table

I have these class models:
class Application(Base):
__tablename__ = 'applications'
identifier = Column(Integer, primary_key=True)
name = Column(String)
description = Column(String)
level1 = relationship("Teams", secondary='assoc_apps_teams', back_populates='support_1')
level2 = relationship("Teams", secondary='assoc_apps_teams', back_populates='support_2')
class Teams(Base):
__tablename__ = 'teams'
identifier = Column(Integer, primary_key=True)
name = Column(String)
description = Column(String)
support_1 = relationship("Application", secondary='assoc_apps_teams', back_populates='level1')
support_2 = relationship("Application", secondary='assoc_apps_teams', back_populates='level2')
class AssocAppsTeams(Base, DictSerializable):
__tablename__ = 'assoc_apps_teams'
identifier = Column(Integer, primary_key=True)
apps_id = Column(Integer, ForeignKey("applications.identifier"), nullable=False)
support1_id = Column(Integer, ForeignKey("teams.identifier"), nullable=False)
support2_id = Column(Integer, ForeignKey("teams.identifier"), nullable=False)
if __name__ == "__main__":
app = model.Application("app", "desc")
session.add(app)
session.commit()
session.close()
Consedering that an application has 2 support levels, each level can have one or more than one team ( each support is a team).
When I run my script I get this error:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Applications.support1 - there are multiple foreign key paths linking the tables via secondary table 'assoc_apps_levels'. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference from the secondary table to each of the parent and child tables.
In this case, I have a relationship many to many: an application is managed by two levels of support that are teams, and each support can manage more than one application. How to map this relationship correctly??

Multiple relationships on composite foreign keys in sqlalchemy

I am trying to create an edge type class to link two nodes. Each of the nodes is a class by itself and both nodes and edge should be part of the same graph. I keep getting
Could not determine join condition between parent/child tables on relationship TypeiNode.edges - there are no foreign keys linking these tables.
even though the foreign keys are defined. I tried defining foreign keys on the node table edges relationship but got the same result. Can someone tell me what is wrong with this example.
class Edge(Base):
__tablename__ = 'edge'
id = Column(Integer, primary_key=True)
graph_id = Column(Integer, ForeignKey('graph.id'))
graph= relationship('Graph', back_populates='graph_edges')
type_1_node_id = Column(Integer, ForeignKey('type_1_node.id'))
type_1_node = relationship('Type1Node',
back_populates='edges',
foreign_keys=[type_1_node_id, graph_id])
type_2_node_id = Column(Integer, ForeignKey('type_2_node.id'))
type_2_node = relationship('Type2Node',
back_populates='edges',
foreign_keys=[type_2_node_id, graph_id ])
__table_args__ = (
ForeignKeyConstraint(
['type_1_node_id', 'graph_id'],
['type_1_node.id','type_1_node.graph_id']),
ForeignKeyConstraint(
['type_2_node_id', 'graph_id'],
['type_2_node.id','type_2_node.graph_id']),
)
class Type1Node(Base):
__tablename__ = 'type_1_node'
id = Column(Integer, primary_key=True)
graph_id = Column(Integer, ForeignKey('graph.id'))
graph = relationship('Graph', back_populates='graph_1_nodes')
edges = relationship('Edge', back_populates='type_1_node')
class Type2Node(Base):
__tablename__ = 'type_2_node'
id = Column(Integer, primary_key=True)
graph_id = Column(Integer, ForeignKey('graph.id'))
graph = relationship('Graph', back_populates='graph_2_nodes')
edges = relationship('Edge', back_populates='type_2_node')
If you read the error carefully, you'll notice it's not complaining about the relationships that you added the foreign_keys argument to, but the ones you didn't:
Could not ... on relationship **TypeiNode.edges** ...
I take
I tried defining foreign keys on the node table edges relationship but got the same result.
to mean that you did not try defining both ends at the same time. So the solution is to define the foreign keys in both ends:
class Type1Node(Base):
__tablename__ = 'type_1_node'
id = Column(Integer, primary_key=True)
graph_id = Column(Integer, ForeignKey('graph.id'))
graph = relationship('Graph')
edges = relationship('Edge', back_populates='type_1_node',
foreign_keys=[Edge.type_1_node_id, Edge.graph_id])

Python/SqlAlchemy 3-way circular dependency

I am having an issue with creating a set of relationships among 3 of my tables. When I run the code to create the tables I get a circular dependency error.
I tried fiddling around with use_alter and post_update based on responses to similar posts, but I wasn't able to solve the issue.
Basically a map has a set of locations, a character has a set of maps, but a character is also located on one of those map locations.
In addition, a map can have parent/child relationships with other maps.
class Character(Base):
__tablename__ = 'character'
ID = Column(Integer, primary_key=True)
name = Column(Unicode(255), nullable=False)
classID = Column(Integer, nullable=False)
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
profileID = Column(Integer, ForeignKey('profile.ID'), nullable=False)
locationID = Column(Integer, ForeignKey('location.ID'))
location = relationship("Location")
maps = relationship("Map", backref="owner", cascade="save-update, merge, delete, delete-orphan")
class Map(Base):
__tablename__ = 'map'
ID = Column(Integer, primary_key=True)
name = Column(Unicode(255))
maptypeID = Column(Integer, nullable=False)
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
parentID = Column(Integer, ForeignKey('map.ID'))
ownerID = Column(Integer, ForeignKey('character.ID'))
children = relationship("Map", backref=backref("parent", remote_side="Map.ID"))
locations = relationship("Location", backref='map', cascade="save-update, merge, delete, delete-orphan")
class Location(Base):
__tablename__ = 'location'
ID = Column(Integer, primary_key=True)
x = Column(Integer, nullable=False)
y = Column(Integer, nullable=False)
locationtypeID = Column(Integer, nullable=False)
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
mapID = Column(Integer, ForeignKey('map.ID'), nullable=False)
How can I solve this issue?
Edit (Solved):
After some more playing around with use_alter, I was able to solve the problem by changing the mapID definition in the Location class from:
mapID = Column(Integer, ForeignKey('map.ID'), nullable=False)
To:
mapID = Column(Integer, ForeignKey('map.ID', use_alter=True, name="fk_location_map"), nullable=False)
In response to the recommendation of breaking the cyclic dependency, I'd rather have the correct relationships and data integrity represented in the schema. I'd much rather worry about fixing ORM issues or changing ORMs rather than fudging the schema to fit the ORM's expectations.
In this particular case, I can't think of a more concise and elegant way to represent all of the information(in the most fundamental sense of the word) that I need the schema to represent for the application.
Side note: Having worked with other languages/frameworks/ORMs, this kind of snafu is often automatically accounted for by the ORM. For example, in .NET E/F, I believe the FK constraints are typically added and activated after all the table creation statements.
You can probably get alembic to help you with a cyclic dependency in your schema, but I'm not going to help you with that. Instead, I'm going to strongly urge you to break the cyclic dependency.
If i'm understanding your schema, you have the rooms or buildings in your model represented by Location. the players/monsters in your schema are Characters, which at any time must be in exactly one location. Additionally, there are some Map items floating around, which are always about some specific location, and are always in the inventory of exactly one character.
So here's how I'd spell that
class Location:
id = Column(Integer, primary_key=True)
class Character:
id = Column(Integer, primary_key=True)
location_id = Column(ForeignKey(Location.id))
location = relationship(Location, backref="characters")
class Map:
id = Column(Integer, primary_key=True)
location_id = Column(ForeignKey(Location.id))
owner_id = Column(ForeignKey(Character.id))
location = relationship(Location, backref="maps")
owner = relationship(Character, backref="inventory")

How to build many-to-many relations using SQLAlchemy: a good example

I have read the SQLAlchemy documentation and tutorial about building many-to-many relation but I could not figure out how to do it properly when the association table contains more than the 2 foreign keys.
I have a table of items and every item has many details. Details can be the same on many items, so there is a many-to-many relation between items and details
I have the following:
class Item(Base):
__tablename__ = 'Item'
id = Column(Integer, primary_key=True)
name = Column(String(255))
description = Column(Text)
class Detail(Base):
__tablename__ = 'Detail'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(String)
My association table is (It's defined before the other 2 in the code):
class ItemDetail(Base):
__tablename__ = 'ItemDetail'
id = Column(Integer, primary_key=True)
itemId = Column(Integer, ForeignKey('Item.id'))
detailId = Column(Integer, ForeignKey('Detail.id'))
endDate = Column(Date)
In the documentation, it's said that I need to use the "association object". I could not figure out how to use it properly, since it's mixed declarative with mapper forms and the examples seem not to be complete. I added the line:
details = relation(ItemDetail)
as a member of Item class and the line:
itemDetail = relation('Detail')
as a member of the association table, as described in the documentation.
when I do item = session.query(Item).first(), the item.details is not a list of Detail objects, but a list of ItemDetail objects.
How can I get details properly in Item objects, i.e., item.details should be a list of Detail objects?
From the comments I see you've found the answer. But the SQLAlchemy documentation is quite overwhelming for a 'new user' and I was struggling with the same question. So for future reference:
ItemDetail = Table('ItemDetail',
Column('id', Integer, primary_key=True),
Column('itemId', Integer, ForeignKey('Item.id')),
Column('detailId', Integer, ForeignKey('Detail.id')),
Column('endDate', Date))
class Item(Base):
__tablename__ = 'Item'
id = Column(Integer, primary_key=True)
name = Column(String(255))
description = Column(Text)
details = relationship('Detail', secondary=ItemDetail, backref='Item')
class Detail(Base):
__tablename__ = 'Detail'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(String)
items = relationship('Item', secondary=ItemDetail, backref='Detail')
Like Miguel, I'm also using a Declarative approach for my junction table. However, I kept running into errors like
sqlalchemy.exc.ArgumentError: secondary argument <class 'main.ProjectUser'> passed to to relationship() User.projects must be a Table object or other FROM clause; can't send a mapped class directly as rows in 'secondary' are persisted independently of a class that is mapped to that same table.
With some fiddling, I was able to come up with the following. (Note my classes are different than OP's but the concept is the same.)
Example
Here's a full working example
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, Session
# Make the engine
engine = create_engine("sqlite+pysqlite:///:memory:", future=True, echo=False)
# Make the DeclarativeMeta
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
projects = relationship('Project', secondary='project_users', back_populates='users')
class Project(Base):
__tablename__ = "projects"
id = Column(Integer, primary_key=True)
name = Column(String)
users = relationship('User', secondary='project_users', back_populates='projects')
class ProjectUser(Base):
__tablename__ = "project_users"
id = Column(Integer, primary_key=True)
notes = Column(String, nullable=True)
user_id = Column(Integer, ForeignKey('users.id'))
project_id = Column(Integer, ForeignKey('projects.id'))
# Create the tables in the database
Base.metadata.create_all(engine)
# Test it
with Session(bind=engine) as session:
# add users
usr1 = User(name="bob")
session.add(usr1)
usr2 = User(name="alice")
session.add(usr2)
session.commit()
# add projects
prj1 = Project(name="Project 1")
session.add(prj1)
prj2 = Project(name="Project 2")
session.add(prj2)
session.commit()
# map users to projects
prj1.users = [usr1, usr2]
prj2.users = [usr2]
session.commit()
with Session(bind=engine) as session:
print(session.query(User).where(User.id == 1).one().projects)
print(session.query(Project).where(Project.id == 1).one().users)
Notes
reference the table name in the secondary argument like secondary='project_users' as opposed to secondary=ProjectUser
use back_populates instead of backref
I made a detailed writeup about this here.
Previous Answer worked for me, but I used a Class base approach for the table ItemDetail. This is the Sample code:
class ItemDetail(Base):
__tablename__ = 'ItemDetail'
id = Column(Integer, primary_key=True, index=True)
itemId = Column(Integer, ForeignKey('Item.id'))
detailId = Column(Integer, ForeignKey('Detail.id'))
endDate = Column(Date)
class Item(Base):
__tablename__ = 'Item'
id = Column(Integer, primary_key=True)
name = Column(String(255))
description = Column(Text)
details = relationship('Detail', secondary=ItemDetail.__table__, backref='Item')
class Detail(Base):
__tablename__ = 'Detail'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(String)
items = relationship('Item', secondary=ItemDetail.__table__, backref='Detail')

Categories