multiple/split class associations in sqlalchemy - python

I have the following objects and relations defined. This is actually quite a simple case, and I am providing all those fields just to show why I believe inhalation and injection anesthesia should be defined by two different classes.
class InhalationAnesthesia(Base):
__tablename__ = "inhalation_anesthesias"
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
concentration = Column(Float)
concentration_unit = Column(String)
duration = Column(Float)
duration_unit = Column(String)
class TwoStepInjectionAnesthesia(Base):
__tablename__ = "twostep_injection_anesthesias"
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
solution_concentration = Column(Float)
solution_concentration_unit = Column(String)
primary_dose = Column(Float)
primary_rate = Column(Float)
primary_rate_unit = Column(String)
secondary_rate = Column(Float)
secondary_rate_unit = Column(String)
class Operation(Base):
__tablename__ = "operations"
id = Column(Integer, primary_key=True)
anesthesia_id = Column(Integer, ForeignKey('inhalation_anesthesias.id'))
anesthesia = relationship("InhalationAnesthesia", backref="used_in_operations")
I would, however, like to define the anesthetic attribute of the Operation class in such a way that any Operation object can point to either a TwoStepInjectionAnesthesia object or an InhalationAnesthesia object.
How can I do that?

I suggest you to use inheritance. It's very, very well explained in SqlAlchemy docs here and here
My recommendation is to create an Anesthesia class and make both InhalationAnesthesia and TwoStepInjectionAnesthesia inherit from it. It's your call to decide which type of table inheritance use:
single table inheritance
concrete table inheritance
joined table inheritance
The most common forms of inheritance are single and joined table,
while concrete inheritance presents more configurational challenges.
For your case I'm asuming joined table inheritance is the election:
class Anesthesia(Base)
__tablename__ = 'anesthesias'
id = Column(Integer, primary_key=True)
anesthetic = Column(String)
# ...
# every common field goes here
# ...
discriminator = Column('type', String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
The purpose of discriminator field:
... is to act as the discriminator, and stores
a value which indicates the type of object represented within the row.
The column may be of any datatype, though string and integer are the
most common.
__mapper_args__'s polymorphic_on key define which field use as discriminator.
In children classes (below), polymorphic_identity key define the value that will be stored in the polymorphic discriminator column for instances of the class.
class InhalationAnesthesia(Anesthesia):
__tablename__ = 'inhalation_anesthesias'
__mapper_args__ = {'polymorphic_identity': 'inhalation'}
id = Column(Integer, ForeignKey('anesthesias.id'), primary_key=True)
# ...
# specific fields definition
# ...
class TwoStepInjectionAnesthesia(Anesthesia):
__tablename__ = 'twostep_injection_anesthesias'
__mapper_args__ = {'polymorphic_identity': 'twostep_injection'}
id = Column(Integer, ForeignKey('anesthesias.id'), primary_key=True)
# ...
# specific fields definition
# ...
Finally the Operation class may reference the parent table Anesthesia with a typical relationship:
class Operation(Base):
__tablename__ = 'operations'
id = Column(Integer, primary_key=True)
anesthesia_id = Column(Integer, ForeignKey('anesthesias.id'))
anesthesia = relationship('Anesthesia', backref='used_in_operations')
Hope this is what you're looking for.

Related

How can I access subclasses from upper class in sqlalchemy?

I have 3 classes;
'Company' top class its subclass 'Department' its subclass 'DepartmentalUnit'
I can access the values โ€‹โ€‹of all classes from the 'DepartmentalUnit' class to the top class 'Company'
What I could not do and understand despite reading the document is that;;
I cannot access other subclasses from the 'company' class
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
departments = relationship('Department',backref='company')
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
department_name = Column(String)
company_id = Column(Integer, ForeignKey('company.id'))
departmentalunits = relationship('DepartmentalUnit', backref='department')
class DepartmentalUnit(Base):
__tablename__ = 'departmentalunit'
id = Column(Integer, primary_key=True,nullable=False)
departmental_unit_name = Column(String)
departments_id = Column(Integer, ForeignKey('department.id'))
The code from which I access the upper classes from the subclasses:
query = session.query(DepartmentalUnit)
instance = query.all()
for i in instance:
print(i.department.company.name)
print(i.department.department_name)
print(i.departmental_unit_name)
The code I can't access other subclasses from the company class:
query = session.query(Company)
instance = query.all()
for i in instance:
print(i.department.department_name)
Your last query should be used differently:
there is a typo in the name of the relationship: should be departments instead of department
given that the relationship is 1-N, the result is a list, so you should iterate over children.
This should work:
query = session.query(Company)
for company in query.all():
print(company.name)
for dep in company.departments:
print(" ", dep.department_name)
for dep_unit in dep.departmentalunits:
print(" ", dep_unit.departmental_unit_name)
I solved the problem. I added a backref to relationships and now I can access all of them from the company. Not sure if it's a correct method? However, I am currently getting the return I want. I have no unanswered request yet.
Example solved:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
departments = relationship('Department',backref='company',uselist=False)
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
department_name = Column(String)
company_id = Column(Integer, ForeignKey('company.id'))
departmentalunits = relationship('DepartmentalUnit', backref='department',uselist=False)
class DepartmentalUnit(Base):
__tablename__ = 'departmentalunit'
id = Column(Integer, primary_key=True,nullable=False)
departmental_unit_name = Column(String)
departments_id = Column(Integer, ForeignKey('department.id'))
query = session.query(Company)
instance = query.all()
for i in instance:
print(f"Company: {i.name}")
print(f"Department: {i.departments.department_name}")
print(f"Department Unit: {i.departments.departmentalunits.departmental_unit_name}")
print( f"Report Category : {i.departments.departmentalunits.reportcategoryoftheunit.report_category_name}")

SQLAlchemy - querying multiple tables and returning nested objects

Suppose we have a simple one-to-many relationship between Company and Employee, is there a way to query all companies and have a list of employees in the attribute of each company?
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
company_id = Column(Integer, ForeignKey(Company.id))
I'm looking for something like this:
>>> result = db.session.query(Company).join(Employee).all()
>>> result[0].Employee
[<Employee object at 0x...>, <Employee object at 0x...>]
The size of result should be same as the number of rows in company table.
I tried the following and it gives tuple of objects (which makes sense) instead of nice parent / child structure:
>>> db.session.query(Company, Employee).filter(Company.id = Employee.company_id).all()
It's not hard to convert this into my desired object structure but just wanted to see if there's any shortcut.
You have to configure the relationship in the parent class:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
employees = relationship('Employee', lazy='joined') # <<< Add this line
Then you can query it without a join:
companies = session.query(Company).all()
print(companies[0].employees)
Documentation:
https://docs.sqlalchemy.org/en/13/orm/loading_relationships.html
You could do something like this:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
employees = db.session.query(Company, Employee).filter(Company.id = self.id).all()
self.employee_list = ['{0} {1}'.format(c.first_name, c.last_name) for c in employees]
Then you could access a employee name with Company.employee_list[0]

Sqlalchemy foreign key to a subset of polymorphic classes

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

From Elixir to SqlAlchemy Declarative, Polymorphic Nodes with Child Inheritance [duplicate]

This question already has an answer here:
Creating container relationship in declarative SQLAlchemy
(1 answer)
Closed 3 years ago.
I have already finished a good bit of my python/Elixir interface on my existing database. I am now considering to drop Elixir and move everything into pure SQLAlchemy, most likely wanting to use Declarative methods.
I am not sure where to even start with this particular inheritance relationship. I don't think sqlalchemy performs inheritance in this manner (or as "magically"), and I am a bit confused how the same would look in sqlalchemy:
This is a polymorphic multi-table join, with each class mapped to its own database table. When finished, another class (not included here) will have a OneToMany with 'Comp'. The Comp subclasses have a Primary Key that is a Foreign key to Comp.id.
class Comp(Entity):
using_options(inheritance='multi')
parent = ManyToOne('Assembly', onupdate='cascade', ondelete='set null')
quantity = Field(Numeric(4), default=1)
def __repr__(self):
return "<Comp>"
## If not familiar with Elixir, each of the following "refid" point to a different
## table depending on its class. This is the primary need for polymorphism.
class CompAssm(Comp):
using_options(inheritance='multi')
refid = ManyToOne('Assembly', onupdate='cascade', ondelete='set null')
def __repr__(self):
return "<CompAssm>"
class CompItem(Comp):
using_options(inheritance='multi')
refid = ManyToOne('Item', onupdate='cascade')
def __repr__(self):
return "<CompItem>"
class CompLabor(Comp):
using_options(inheritance='multi')
refid = ManyToOne('Labor', onupdate='cascade')
def __repr__(self):
return "<CompLabor>"
I think this is the general direction, but may still need tweaking.
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Assembly(Base):
__tablename__ = 'assembly'
assm_id = Column(Integer, primary_key=True)
children = relationship('Comp')
### other assembly stuff
class Comp(Base):
__tablename__ = 'components'
id = Column(Integer, primary_key=True)
comp_type = Column('type', String(50))
__mapper_args__ = {'polymorphic_on': comp_type}
parent = Column(Integer, ForeignKey('assembly.assm_id'))
quantity = Column(Integer)
class CompAssm(Comp):
__tablename__ = 'compassm'
__mapper_args__ = {'polymorphic_identity': 'compassm'}
id = Column(Integer, ForeignKey('components.id'), primary_key=True)
refid = Column(Integer, ForeignKey('assembly.assm_id'))
class CompItem(Comp):
__tablename__ = 'compitem'
__mapper_args__ = {'polymorphic_identity': 'compitem'}
id = Column(Integer, ForeignKey('components.id'), primary_key=True)
refid = Column(Integer, ForeignKey('items.id'))
class CompLabor(Comp):
__tablename__ = 'complabor'
__mapper_args__ = {'polymorphic_identity': 'complabor'}
id = Column(Integer, ForeignKey('components.id'), primary_key=True)
refid = Column(Integer, ForeignKey('labors.id'))

SQLAlchemy - Mapping self-referential relationship as one to many (declarative form)

I want to map a Tag entity using declarative method with SQLAlchemy. A tag can have a parent (another Tag).
I have:
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True)
label = Column(String)
def __init__(self, label, parentTag=None):
self.label = label
How can I add the "parent" relationship?
You add a ForeignKey referencing the parent, and then create a relationship that specifies the direction via remote_side. This is documented under adjacency list relationships. For declarative you'd do something like this:
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True)
label = Column(String)
parent_id = Column(Integer, ForeignKey('tag.id'))
parent = relationship('Tag', remote_side=[id])
If you want the reverse relation also, add backref='children' to the relationship definition.
If you need children, you need to use uselist:
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True)
label = Column(String)
child_id = Column(Integer, ForeignKey('tag.id'))
children = relation('Tag', remote_side=[id], uselist=True)
class Company(BaseModel):
__tablename__ = 'companies'
companyName = db.Column(db.String(50))
contactPerson = db.Column(db.String(50))
email = db.Column(db.String(50))
mobile = db.Column(db.String(20))
parentID = db.Column(db.Integer, db.ForeignKey('companies.id')) # parent company ID
childrenCompany = db.relationship('Company', remote_side='Company.id',
backref=db.backref('children_company')) # parent Company
use:
In [2]: company_query = Company.query.get_or_404(1)
In [3]: company_query.children_company
Out[3]:
[<app.models.user.Company at 0x10f527850>,
<app.models.user.Company at 0x10f527c10>]
parent = relation('Tag') โ€” see http://www.sqlalchemy.org/docs/05/reference/ext/declarative.html#configuring-relations.

Categories

Resources