SQLAlchemy relationships: more than one self-referenced columns - python

There is an sqlalchemy object
class Employee(Base):
__tablename__ = "employee"
# Columns
id = Column(Integer, primary_key=True, autoincrement=True)
representative_id = Column(Integer, ForeignKey('employee.id'))
parent_id = Column(Integer, ForeignKey('employee.id'))
affiliate_id = Column(Integer, ForeignKey('employee.id'))
# Relatonships
representative = relationship("Employee", ????)
parent = relationship("Employee", ????)
affiliate = relationship("Employee", ????)
How to configure the relationships in the right way if an employee can have, 0 or 1 parent, affiliate and representative? Do know these three are different ones. DB - MySQL

I found out how do to it by myself:
class Employee(Base):
__tablename__ = "employee"
# Columns
id = Column(Integer, primary_key=True, autoincrement=True)
representative_id = Column(Integer, ForeignKey('employee.id'))
parent_id = Column(Integer, ForeignKey('employee.id'))
affiliate_id = Column(Integer, ForeignKey('employee.id'))
# Relatonships
representative = relationship("Employee",
primaryjoin="Employee.representative_id==Employee.id",
remote_side=[id])
parent = relationship("Employee",
primaryjoin="Employee.parent_id==Employee.id",
remote_side=[id])
affiliate = relationship("Employee",
primaryjoin="Employee.affiliate_id==Employee.id",
remote_side=[id])

Related

SQLAlchemy multiple foreign keys on the same column

I am working with the following database table design. The relationship is one-to-many, parent to child. So one parent (mother or father) has multiple children. I'm having issues mapping these tables because the child's column parent_id is a Foreign Key to both of the parent tables.
class Mother(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
class Father(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
Class Children(Base):
id = Column(Integer, primary_key=True)
unique_id = Column(Integer, ForeignKey('mother.child_id')
I have tried defining a relationship with backref in the parent tables to avoid explicitely defining Foreign Keys in the child tables:
class Mother(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
children = relationship('Child', backref='mother')
class Father(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
children = relationship('Child', backref='father')
Class Children(Base):
id = Column(Integer, primary_key=True)
unique_id = Column(Integer)
But this yields a one-to-one relationship.
I think that foreign keys should bo on Child table, it would be better and more realistic model:
class Mother(Base):
id = Column(Integer, primary_key=True)
children = relationship('Child', backref='mother')
class Father(Base):
id = Column(Integer, primary_key=True)
children = relationship('Child', backref='father')
class Child(Base):
id = Column(Integer, primary_key=True)
father_id = Column(Integer, ForeignKey('father.id'))
mother_id = Column(Integer, ForeignKey('mother.id'))
In above model, each child has at most one mother and one father, while a mother/father can have many children.

How to autofill table with foreign keys in SQLAlchemy using relationships?

Using SQLAlchemy I have three models: Parent1, Parent2, and Child, where Parent1 has one-to-one relationship with Parent2, and both of them has the same relationship with Child. Here are they:
from extensions import db_session
class Parent1(Base):
__tablename__ = 'parent1'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent2 = relationship("Parent2", backref="parent1", uselist=False)
child = relationship("Child", backref="parent1", uselist=False)
class Parent2(Base):
__tablename__ = 'parent2'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
child = relationship("Child", backref="parent2", uselist=False)
class Child(Base):
id = Column(Integer, primary_key=True)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
parent2_id = Column(Integer, ForeignKey('parent2.id'))
What I am trying to achieve is to fill Child table with its parents' foreign keys.
So, when I execute this:
parent1 = Parent1(name="Adil")
parent1.parent2 = Parent2(name="Aisha")
parent1.child = Child()
db_session.add(parent1)
db_session.commit()
to the parents tables it inserts data as needed, however to the Child table it inserts data like this:
Child
id parent1_id parent2_id
1 1 NULL
How to properly set relationships, so that on any insert to the Parent1->Parent2 tables it also inserts its ids as foreign keys to Child table?
What I want to achieve is:
Child
id parent1_id parent2_id
1 1 1
I came up with this solution. I removed declared model Child and created association table between two models and added secondary parameter filling with assocation table name:
Base = declarative_base()
child = Table('child', Base.metadata,
Column('parent1_id', Integer, ForeignKey('parent1.id')),
Column('parent2_id', Integer, ForeignKey('parent2.id')))
class Parent1(Base):
__tablename__ = 'parent1'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent2 = relationship("Parent2", secondary='child', uselist=False)
class Parent2(Base):
__tablename__ = 'parent2'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent1_id = Column(Integer, ForeignKey('parent1.id'))

Deleting from self-referential inherited objects does not cascade in SQLAlchemy-SQLite

I posted a question previously here where I tried to build a hierarchy using different objects. Each object can have any type of object as it's parent, and any type as children. I solved it by using the Node class suggested by SQLAlchemy here and letting the other objects inherit from it.
Now I'm having the problem that deleting a node does not delete it's children. I have tried a lot of solutions like different cascade settings, using ondelete='CASCADE' in the foreignkey, as well as DBSession.execute('pragma foreign_keys=on') but none are working. I think the problem is in the ParentID key because in the child it is not null when the parent is delete.
I'm pretty new to SQLAlchemy so I'm not at all sure where I'm going wrong, any help would be appreciated.
These are my current models:
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
# DBSession.execute('pragma foreign_keys=on')
Base = declarative_base()
class Node(Base):
def getID():
return uuid.uuid1().hex
__tablename__ = 'Node'
ID = Column(Text, primary_key=True, default=getID)
ParentID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'))
type = Column(Text(50))
Children = relationship("Node",
backref=backref('Parent',
remote_side=[ID]
),
single_parent=True,
cascade="all, delete, delete-orphan",
passive_deletes = True
)
__mapper_args__ = {
'polymorphic_identity':'Node',
'polymorphic_on':type
}
class A(Node):
__tablename__ = 'A'
ID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'), primary_key=True)
Name = Column(Text)
Description = Column(Text)
__mapper_args__ = {'polymorphic_identity':'A'}
class B(Node):
__tablename__ = 'B'
ID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'), primary_key=True)
Name = Column(Text)
Description = Column(Text)
__mapper_args__ = {'polymorphic_identity':'B'}
class C(Node):
__tablename__ = 'C'
ID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'), primary_key=True)
Name = Column(Text)
Description = Column(Text)
Quantity = Column(Integer)
Rate = Column(Integer)
__mapper_args__ = {'polymorphic_identity':'C' }
This is how I build a hierarchy:
a = A(Name="PName",
Description="PDesc",
ParentID='0')
b = B(Name="BGName",
Description="BGDesc",
ParentID=project.ID)
c = C(Name="BIName",
Description="BIDesc",
Quantity=10,
Rate=5,
ParentID=budgetgroup.ID)
# Append the children nodes to their parents
b.Children.append(c)
a.Children.append(b)
DBSession.add(a)
And this is how I delete it:
def deleteitem(id):
deleteid = id
deletethis = DBSession.query(Node).filter_by(ID=deleteid).first()
qry = DBSession.delete(deletethis)
# qry = DBSession.query(Node).filter_by(ID=deleteid).delete(
# synchronize_session='fetch')
transaction.commit()
Note: neither the one way or the other commented out delete cascades.
I was able to find a solution from this answer here.
Now my Node class looks as follows:
class Node(Base):
__tablename__ = 'Node'
ID = Column(Integer, primary_key=True)
ParentID = Column(Integer, ForeignKey('Node.ID', ondelete='CASCADE'))
type = Column(Text(50))
Children = relationship(
'Node',
cascade="all",
backref=backref("Parent", remote_side='Node.ID'),
)
__mapper_args__ = {
'polymorphic_identity':'Node',
'polymorphic_on':type
}
And this seems to work, all my deletes are cascading.

SQLAlchemy - Relationship across four tables?

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.

sqlalchemy constraint in models 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),
)

Categories

Resources