I have a one to many relationship in sqlalchemy but I don't get the inserts to work properly. I have tried to make a minimal example here:
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker
db = create_engine('sqlite://')
db.echo = True
metadata = MetaData(db)
Base = declarative_base()
Session = sessionmaker(bind=db)
session = Session()
class Child(Base):
__table__ = Table('child', Base.metadata,
Column('id', Integer, primary_key=True),
Column('parent_id', Integer),
Column('name', String(50))
)
class Parent(Base):
__table__ = Table('parent', Base.metadata,
Column('id', Integer, primary_key=True),
Column('name', String(50))
)
children = relationship(Child, primaryjoin="Parent.id == Child.parent_id",
foreign_keys=[__table__.c.id])
Base.metadata.create_all(db)
c = Child(id=1, name="C")
p = Parent(id=1, name="P", children=[c])
session.add(p)
session.commit()
Running this gives AttributeError: 'list' object has no attribute '_sa_instance_state' from session.add(p).
I tried changing the classes to this:
class Child(Base):
__tablename__ = 'child'
id = Column('id', Integer, primary_key=True)
parent_id = Column('parent_id', Integer, ForeignKey('parent.id'))
name = Column('name', String(50))
class Parent(Base):
__tablename__ = 'parent'
id = Column('id', Integer, primary_key=True)
name = Column('name', String(50))
children = relationship(Child, backref="parent")
and then it works. I specify that the parent_id is a foreign key there and use the backref syntax. However in my production code the Parent table is a temporary table so I can't directly reference it using a ForeignKey. So whats wrong with the first code block and how can it be fixed ?
From the SQLAlchemy relationship API in the docs:
That is, if the primaryjoin condition of this relationship() is a.id == b.a_id, and the values in b.a_id are required to be present in a.id, then the “foreign key” column of this relationship() is b.a_id.
In your example, child.parent_id is required to be present in parent.id. So your "foreign key" column is child.parent_id.
Therefore, changing:
foreign_keys=[__table__.c.id]
to this:
foreign_keys=[Child.__table__.c.parent_id]
should solve your problem.
Related
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. 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?
Starting from the many-to-many relationship example from the SQLAlchemy documentation, I want to add an attribute first_child that will return the first child of children defined by the relationship. The first_child attribute needs to be useable in an association_proxy attribute definition such as first_child_id below.
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child", secondary=association_table)
first_child = ???
first_child_id = association_proxy('first_child', 'id')
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
I'm thinking I need a to declare first_child as either a hybrid_property or a column_property, but I don't know how to return the first element.
In addition to first_child, I also need last_child and an associated last_child_id attribute.
I'm using SQLAlchemy with a MySQL database.
If what you need is to have minimum start_time and maximum end_time, then I would use column_property just for these columns:
association_table = Table(
'association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
start_time = Column(DateTime)
end_time = Column(DateTime)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship(
"Child",
secondary=association_table,
backref="parents",
)
min_start_time = column_property(
select([Child.start_time.label("min_start_time")])
.where(Child.id == association_table.c.right_id)
.where(id == association_table.c.left_id)
.order_by(Child.start_time.asc())
.limit(1)
)
max_end_time = column_property(
select([Child.end_time.label("max_end_time")])
.where(Child.id == association_table.c.right_id)
.where(id == association_table.c.left_id)
.order_by(Child.end_time.desc())
.limit(1)
.as_scalar()
)
But if you need more than one such special column from the relationship, probably it would be more efficient to use hybrid_property.
The problem with association_proxy is that you cannot use it on hybrid_properties (or at least not in a direct manner).
A simple solution might be to use a property decorator, which would be evaluated on an already-loaded instance:
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child", secondary=association_table)
#property
def first_child(self):
return children[0]
However, you will not be able to use it on queries and it is a "read only" property. More information on SQLAlchemy plain-descriptor.
Given the following tables below, how do I query for all groups of grouptype 'foo' for user_x?
The equivalent SQL would be something like:
SELECT * FROM users_to_groups
LEFT JOIN users ON user_id=users.id
LEFT JOIN groups ON group_id=groups.id
WHERE groups.type='type1' AND user_id=1;
I was thinking the SQLAlchemy query would look something like:
session.query(UserGroup).filter(UserGroup.user==user_x,
UserGroup.group.grouptype=='foo')
but I don't know how to specify the grouptype (the above query raises this exception: AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with UserGroup.group has an attribute 'grouptype')
users_to_groups = Table(
'users_to_groups', Base.metadata,
Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
Column('group_id', Integer, ForeignKey('groups.id'), primary_key=True),
)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
groups = relationship('Group',
secondary=users_to_groups,
backref=backref('users',
collection_class=set),
collection_class=set)
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
grouptype = Column(String)
Base.metadata.create_all(engine)
class UserGroup(object):
pass
mapper(UserGroup, users_to_groups,
properties={
'group' : relationship(Group),
'user' : relationship(User),
})
I'm using sqlalchemy 0.8.2 with Postgres 9.2.4.
You can use RelationshipProperty.Comparator.has():
session.query(UserGroup).filter(UserGroup.user == user_x,
UserGroup.group.has(Group.grouptype == 'foo'))
You may find it more natural to query for Group directly:
session.query(Group).filter(Group.users.any(User.id == user_x),
Group.grouptype == 'foo')
I'm quite new to SQLAlchemy (and I do not have much experience with databases in general). I'm trying to traversere two many-to-many relationships. Given a parent, how can I get all unique grandchildren?
parent_child_table = Table('parent_child', Base.metadata,
Column('parent_id', Integer, ForeignKey('parent.id')),
Column('child_id', Integer, ForeignKey('child.id'))
)
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=parent_child_table,
backref="parents")
child_grandchild_table = Table('child_grandchild', Base.metadata,
Column('child_id', Integer, ForeignKey('child.id')),
Column('grandchild_id', Integer, ForeignKey('grandchild.id'))
)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
grandchildren = relationship("Grandchild",
secondary=child_grandchild_table,
backref="children")
class Grandchild(Base):
__tablename__ = 'grandchild'
id = Column(Integer, primary_key=True)
Thanks! This problem is giving me a headache...
The most straight-forard way:
# my_parent = ... (instance of Parent)
q = (session.query(Grandchild)
.join(Child, Grandchild.children)
.join(Parent, Child.parents)
.filter(Parent.id == my_parent.id)
)
sqlalchemy will return only unique Grandchild instances (although the SQL query does not filter duplicates out).