I'm trying to create a Self-Referential Many-to-Many Relationship. The example outlined in the SQLAlchemy documentation works great. Here are the models I created:
from sqlalchemy import Integer, ForeignKey, String, Column, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
Table('NodeToNode', Base.metadata,
Column('leftNodeId', Integer, ForeignKey('Node.id'), primary_key=True),
Column('rightNodeId', Integer, ForeignKey('Node.id'), primary_key=True)
)
class Node(Base):
__tablename__ = 'Node'
id = Column(Integer, primary_key=True)
label = Column(String)
rightNodes = relationship('Node',
secondary='NodeToNode',
primaryjoin='Node.id==NodeToNode.c.leftNodeId',
secondaryjoin='Node.id==NodeToNode.c.rightNodeId',
backref='leftNodes'
)
And the script for adding data in:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
engine = create_engine('sqlite:///practice.sqlite3')
session = Session(bind=engine)
nodes = [
Node(label='A'),
Node(label='B'),
Node(label='C'),
Node(label='D'),
Node(label='E'),
]
nodes[0].rightNodes = [nodes[1], nodes[3], nodes[2]]
nodes[0].leftNodes = [nodes[4]]
session.add_all(nodes)
session.commit()
I want to add a column to the association table so I'd assume I need to convert the association table to its own class:
class NodeToNode(Base):
__tablename__ = 'NodeToNode'
leftNodeId = Column(Integer, ForeignKey('Node.id', onupdate='CASCADE'), primary_key=True)
rightNodeId = Column(Integer, ForeignKey('Node.id', onupdate='CASCADE'), primary_key=True)
sortOrder = Column(Integer, nullable=False)
This, however, results in the following error:
sqlalchemy.exc.InvalidRequestError: Class <class 'models.node.NodeToNode'> does not have a mapped column named 'c'
Any idea what I'm doing wrong here?
Related
Im trying to create a relationship between two tables which are in separated classes.
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(50))
age = Column(Integer)
So I want to create a table called order which FK is the user id.
I did this:
from sqlalchemy import (Column, Float, ForeignKey, Integer, String,
create_engine)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker
engine = create_engine(
"mysql+pymysql://root:admin#localhost/test", echo=False)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
class Order(Base):
__tablename__ = 'order'
product = Column(String(50))
price = Column(Float)
user_id = Column(
Integer,
ForeignKey('user.id', ondelete='CASCADE'), primary_key=True,
nullable=False,
# no need to add index=True, all FKs have indexes
)
cliente = relationship('User', foreign_keys='Order.user_id')
Base.metadata.create_all(engine)
I get this error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'order.user_id' could not find table 'user' with which to generate a foreign key to target column 'id'
you need to add db.relationship in User table not in order table hopefully you get this useful.
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...
I'm trying to create a self-referential many-to-many relationship table with an extra column called sort_order:
from sqlalchemy import Integer, ForeignKey, String, Column, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.orderinglist import ordering_list
Base = declarative_base()
class Node2Node(Base):
__tablename__ = "NodeToNode"
left_node_id = Column(Integer, ForeignKey("Node.id"), primary_key=True)
right_node_id = Column(Integer, ForeignKey("Node.id"), primary_key=True)
sort_order = Column(Integer, nullable=False)
class Node(Base):
__tablename__ = "Node"
id = Column(Integer, primary_key=True)
label = Column(String)
# Many To Many with Node
right_nodes = relationship(
"Node",
secondary="NodeToNode",
primaryjoin="Node.id==NodeToNode.c.left_node_id",
secondaryjoin="Node.id==NodeToNode.c.right_node_id",
backref="left_nodes",
order_by="NodeToNode.c.sort_order",
)
def __repr__(self):
return f"<Node: {self.label}>"
When I run the following script I get an error saying NOT NULL constraint failed: NodeToNode.sort_order:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
engine = create_engine("sqlite:///practice.sqlite3")
session = Session(bind=engine)
# Build tables
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
# Load in data
nodes = [
Node(label="A"),
Node(label="B"),
Node(label="C"),
Node(label="D"),
Node(label="E"),
]
session.add_all(nodes)
session.commit()
nodes[0].right_nodes = [nodes[3], nodes[1], nodes[2]]
nodes[0].left_nodes = [nodes[4]]
session.add_all(nodes)
session.commit()
# Display data
print("Nodes: {}".format(nodes))
print("Node A right nodes: {}".format(nodes[0].right_nodes))
print("Node A left nodes: {}".format(nodes[0].left_nodes))
session.close()
How can I set this sort_order key based on the item's index in the list?
Use Self-referential many-to-many relationship with Association Object.
See example at https://stackoverflow.com/a/62281276/9387542
I have this design
# coding: utf-8
from sqlalchemy import Column, VARCHAR, ForeignKey, create_engine
from sqlalchemy.dialects.mysql import INTEGER, TINYINT
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.inspection import inspect
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
engine = create_engine('mysql://root:toor#localhost/test', echo=True)
Model = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
class A(Model):
__tablename__ = 'A'
id = Column(INTEGER(unsigned=True), primary_key=True)
name = Column(VARCHAR(32), nullable=False, index=True)
class AB(Model):
__tablename__ = 'AB'
A_id = Column(INTEGER(10, unsigned=True), ForeignKey('A.id'), nullable=False, primary_key=True)
B_id = Column(INTEGER(10, unsigned=True), ForeignKey('B.id'), nullable=False, primary_key=True)
type = Column(TINYINT(3, unsigned=True), nullable=False)
A_rel = relationship(
"A",
foreign_keys=[A_id]
)
B_rel = relationship(
"B",
foreign_keys=[B_id],
)
mapper_AB = inspect(AB)
class B(Model):
__tablename__ = 'B'
id = Column(INTEGER(10, unsigned=True), primary_key=True)
As = relationship("A", secondary=mapper_AB.local_table, uselist=False)
ABs = relationship("AB")
type = association_proxy('ABs', 'type')
Model.metadata.create_all(engine)
a = A(
name="test",
)
session.add(a)
session.commit()
b = B(
As=a,
type=2
)
print(b.type)
session.add(b)
session.commit()
This obviously fails, but I am trying to simplify the design by adding the AB extra information (type) via one (B) of the related classes constructor.
I have gone through tons of SQLAlchemy documentation, and even lost myself inside the source code but to no avail. The 3 table design of the database cannot be modified, as is part of a bigger system and belongs to another project. My goal is to map those table into a more simplified design for further development where this models will be used.
Is this possible using SQLAlchemy mechanisms with declarative style?
I try to add a record to a joined-inheritance table.
For some reason I cannot fathom, the dependent table is not INSERTed into.
## Inheritance test case.
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import Session, relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Employee(Base):
__tablename__ = 'personnel'
__mapper_args__ = { 'polymorphic_on': 'etyp' }
id = Column(Integer, primary_key=True)
name = Column(String(50))
etyp = Column(String(10))
class Engineer(Employee):
__mapper_args__ = { 'polymorphic_identity':'eng' }
__tablename__ = "engineers"
id = Column(Integer, ForeignKey(Employee.id), index=True)
eng_data = Column(String(50))
engine = create_engine('sqlite:///', echo=True)
Base.metadata.create_all(engine)
session = Session(bind=engine)
e1 = Engineer(name="wally", eng_data="lazy guy")
session.add(e1)
session.commit()
# note that the Engineer table is not INSERTed into
e1 = session.query(Employee).filter_by(name="wally").one()
# the next line triggers an exception because it tries to lookup
# the Engineer row, which is missing
print e1.eng_data
This is Python 2.7, sqlalchemy 0.9.4 (Debian testing).
Making engineers.id a primary key will solve the problem:
class Engineer(Employee):
# ...
id = Column(Integer, ForeignKey(Employee.id), primary_key=True)