I have the following models:
class A(Base):
__tablename__ = 'A'
a_id = Column(Integer, primary_key=True)
class B(Base):
__tablename__ = 'B'
b_id = Column(Integer, primary_key=True)
class C(Base):
__tablename__ = 'C'
c_id = Column(Integer, primary_key=True)
class AB(Base):
__tablename__ = 'A_B'
a_id = Column(Integer, ForeignKey(A.a_id), primary_key=True)
b_id = Column(Integer, ForeignKey(B.b_id), primary_key=True)
class BC(Base):
__tablename__ = 'B_C'
b_id = Column(Integer, ForeignKey(B.b_id), primary_key=True)
c_id = Column(Integer, ForeignKey(C.c_id), primary_key=True)
What I want is to add a relationship between A and C using the many-to-many tables b_id as joiner column. I know I can do it using seconday and secondaryjoin when there is a relationship through one many-to-many table, but I have no idea how to do that with 2 many-to-many tables.
I want something like this:
class A(Base):
a_id = Column(Integer, primary_key=True)
c_colletion = relationship(?)
You can use a custom secondary:
ab = AB.__table__
bc = BC.__table__
ac = select([ab.c.a_id, bc.c.c_id]).select_from(ab.join(bc, ab.c.b_id == bc.c.b_id))
A.c_collection = relationship("C", secondary=ac,
primaryjoin=A.a_id == ac.c.a_id,
secondaryjoin=ac.c.c_id == C.c_id)
Related
I am trying to create a join table such that:
Table A.b_id = Table B.id
AND
Table A.c_id = Table C.id
This way I safe space by creating an intermediary "join" table to define the relationship between B and C.
Is the proper way to define this below? I've been looking through the docs but they only define 1:many and many:1 relationships with examples - I don't see any situations such as the one I'm describing. Thanks in advance!
class B(Base):
__tablename__ = "b"
id = Column(Integer, primary_key=True)
class C(Base):
__tablename__ = "c"
id = Column(Integer, primary_key=True)
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
b_id = Column(Integer, ForeignKey("b.id"))
b = relationship("B")
c_id = Column(Integer, ForeignKey("c.id"))
c = relationship("C")
FYI for anyone who's looking in the future. I was able to figure this out by reading the documentation (who woulda guessed?). The proper way to do this is as follows:
from sqlalchemy import Column, ForeignKey, Integer,
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class B(Base):
__tablename__ = "b"
id = Column(Integer, primary_key=True)
a = relationship("A", back_populates("b"))
class C(Base):
__tablename__ = "c"
id = Column(Integer, primary_key=True)
a = relationship("A", back_populates("c"))
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
b_id = Column(Integer, ForeignKey("b.id"))
c_id = Column(Integer, ForeignKey("c.id"))
b = relationship("B", foreign_keys=[b_id], back_populates("a"))
c = relationship("C", foreign_keys=[c_id], back_populates("a"))
First, I used Sqlalchemy's polymorphic architecture.
ChildA and ChildB extends Child.
ChildA has name column.
ChildB has age column.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship(Parent, backref='children')
class ChildA(Child):
__tablename__ = 'child_a'
id = Column(Integer, primary_key=True)
name = Column(String(50))
class ChildB(Child):
__tablename__ = 'child_b'
id = Column(Integer, primary_key=True)
age = Column(Integer)
parent = DBSession.query(Parent).first()
subquery = parent.children.join(ChildA).subquery()
So I want to access ChildA.name column from subquery.
Something like subquery.c.ChildA.name == 'Tom'
If I understood properly what you are trying to do, you don't really need a subquery, it could be simply something like
In [13]:
f = session.query(Parent, ChildA).join(ChildA).first()
print(f.ChildA.name)
Pedro
For the use of subqueries, I would recommend you take a look to sqlalchemy tutorial.
On the other hand, I wasn't able to use the classes as you've defined them, I had to add a ForeignKey like this
class ChildA(Child):
__tablename__ = 'child_a'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
name = Column(String(50))
class ChildB(Child):
__tablename__ = 'child_b'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
age = Column(Integer)
I have no doubt that it works for you, this probably depends on the setup.
And finally, I would like to recommend you to use a column for the type of child. With this, it will be easier to recognize the children you are using. Something like this,
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship(Parent, backref='children')
type = Column(String(20))
__mapper_args__ = {
'polymorphic_identity':'child',
'polymorphic_on':type
}
class ChildA(Child):
__tablename__ = 'child_a'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
name = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'child_a',
}
class ChildB(Child):
__tablename__ = 'child_b'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
age = Column(Integer)
__mapper_args__ = {
'polymorphic_identity':'child_b',
}
Please take a look to sqlalchemy docs for details.
Hope it helps.
Here is my setup:
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True)
class Object(Base):
__tablename__ = 'objects'
object_id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('User.user_id'))
class ObjectHistory(Base):
__tablename__ = 'object_histories'
history_id = Column(Integer, primary_key=True)
object_id = Column(Integer, primary_key=True)
class Action(Base):
__tablename__ = 'actions'
action_id = Column(Integer, primary_key=True)
class ActionDetail(Base):
__tablename__ = 'action_details'
detail_id = Column(Integer, primary_key=True)
history_id = Column(Integer, ForeignKey('ObjectHistory.id')
object = relationship('Object', secondary='object_histories',
primaryjoin='ActionDetail.history_id==ObjectHistory.history_id',
secondaryjoin='Object.object_id==ObjectHistory.object_id',
backref='action_details')
user = relationship('User', ????, backref='action_details')
How can I create the user relationship on ActionDetail? Is it possible? I've tried something similar to the object relationship above it, but to no avail.
I am using sqlalchemy to create a structure that resembles a graph, so that there are several types of nodes and links joining them. The nodes are defined like this:
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
type = Column(Unicode)
__mapper_args__ = {'polymorphic_on': type}
class InputNode(Node):
__tablename__ = 'inputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'input'}
class ThruNode(Node):
__tablename__ = 'thrunode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'thru'}
class OutputNode(Node):
__tablename__ = 'outputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'output'}
Now I want to create a Link table which would looks something like this:
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('node.id', where='type IN ("input", "thru")'))
output = Column(Integer, ForeignKey('node.id', where='type IN ("thru", "output")'))
The bit I'm struggling with is how to do the where part of it, since as I've written it is not valid in sqlalchemy. I had though of using a CheckConstraint or a ForeignKeyConstraint, but I can't see how either of them could actually be used to do this.
I haven't tryed it nor am I an expert of this, but shouldn't this work?
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('thrunode.id'))
output = Column(Integer, ForeignKey('outputnode.id'))
First I had another idea that maybe you could have used different names for the ids and than use those, kind of like:
instead of:
class InputNode(Node):
__tablename__ = 'inputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'input'}
this:
class ThruNode(Node):
[...]
thrunode_id = Column(Integer, ForeignKey('node.id'), primary_key=True)
[...]
and then:
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('node.thrunode_id'))
output = Column(Integer, ForeignKey('node.outputnode_id'))
I got the idea from here sqlalchemy docs: declarative.html#joined-table-inheritance
I have two simple models:
class Message(Backend.instance().get_base()):
__tablename__ = 'messages'
id = Column(Integer, primary_key=True, autoincrement=True)
sender_id = Column(Integer, ForeignKey('users.id'))
content = Column(String, nullable=False)
class ChatMessage(Message):
__tablename__ = 'chat_messages'
id = Column(Integer, ForeignKey('messages.id'), primary_key=True)
receiver_id = Column(Integer, ForeignKey('users.id'))
How to define constraint sender_id!=receiver_id?
This doesn't seem to work with joined table inheritance, I've tried and it complains that the column sender_id from Message doesn't exist when creating the constraint in ChatMessage.
This complaint makes sense, since sender_id wouldn't be in the same table as receiver_id when the tables are created, so the foreign key relationship would need to be followed to check the constraint.
One option is to make ChatMessage a single table.
Use CheckConstraint, placed in table args.
class ChatMessage(Base):
__tablename__ = 'chat_messages'
id = sa.Column(sa.Integer, primary_key=True)
sender_id = sa.Column(sa.Integer, sa.ForeignKey(User.id))
receiver_id = sa.Column(sa.Integer, sa.ForeignKey(User.id))
content = sa.Column(sa.String, nullable=False)
__table_args__ = (
sa.CheckConstraint(receiver_id != sender_id),
)