How to use relationships in SQLAlchemy - python

I am new to SQLAlchemy and I am having troubles implementing the concept of relationships into my logic.
I have two tables: Offices and Departments where departments include the office it exists in.
I tried to apply the information present in the documentation https://www.tutorialspoint.com/sqlalchemy/sqlalchemy_orm_building_relationship.htm but I am having struggles understanding where should I insert the Relationship and if back_populates attribute is actually useful in this case.
class Offices(Base):
__tablename__: 'offices'
id = Column(Integer, primary_key=True)
name = Column(String)
code = Column(String)
reader = Column(Integer)
class Departments(Base):
__tablename__ = 'departments'
id = Column(Integer, primary_key=True)
name = Column(String)
office_id = Column(Integer, ForeignKey('offices.id'))
office = relationship("Offices", back_populates="departments")
def __init__(self, name):
self.name = name
Also, will I have to define anything related to the office in the department's __init__ in order to save it when I try inserting new rows into the table?

You can specify them relationship in both models
class Offices(Base):
__tablename__: 'offices'
id = Column(Integer, primary_key=True)
name = Column(String)
code = Column(String)
reader = Column(Integer)
departments = relationship("Departments", back_populates='office')
class Departments(Base):
__tablename__ = 'departments'
id = Column(Integer, primary_key=True)
name = Column(String)
office_id = Column(Integer, ForeignKey('offices.id'))
office = relationship("Offices", back_populates="departments")
After that you can achieve this (assume department and office are objects)
department.offices # this will refer to office
office.departments # this will refer to departments set (department is one of them)
The alternative is backref. It will create reverse relationship by itself, so
class Offices(Base):
__tablename__: 'offices'
id = Column(Integer, primary_key=True)
name = Column(String)
code = Column(String)
reader = Column(Integer)
class Departments(Base):
__tablename__ = 'departments'
id = Column(Integer, primary_key=True)
name = Column(String)
office_id = Column(Integer, ForeignKey('offices.id'))
office = relationship("Offices", backref="departments")
will do the same the first does

Related

Many to many relationship in flask SQLAlchemy

I'm learning flask and i'm looking to implement a many to many realtionship. I searched on internet but there are different ways,
this is what i have tried so far
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Group_Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey(Group.id))
theory_id = db.Column(db.Integer, db.ForeignKey(Course.id))
I'm not sure this is the right way to do it. I'm having issues with delete endpoint. I think that i should add on delete cascade but i don't know how to do it.
there are some sites that add relationships to the table association, so the association table looks like that.
class Group_Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey(Group.id))
course_id = db.Column(db.Integer, db.ForeignKey(Course.id))
course = db.relationship(Course, backref="course",cascade='all,
delete')
group = db.relationship(Group, backref="group", cascade='all,
delete`)
there are another examples where they are including a relationship field in both tables like that:
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
courses = db.relationship("Course", secondary=course_groups,
back_populates="courses")
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
groups = db.relationship("Group", secondary=course_groups,
back_populates="groups")
So i'm confused, which one is the most correct ?

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]

table name specified more than once

I have the following tables configured:
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String)
class Gadget(Base):
__tablename__ = "gadget"
id = Column(Integer, primary_key=True)
brand = Column(String)
class UserGadget(Base):
__tablename__ = "user_gadget"
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
gadget_id = Column(Integer, ForeignKey('gadget.id'), primary_key=True)
user = relationship("User", backref=backref('userGadgets', order_by=user_id))
gadget = relationship("Gadget", backref=backref('userGadgets', order_by=gadget_id))
class GadgetComponent(Base):
__tablename__ = "gadget_component"
id = Column(String, primary_key=True)
gadget_id = Column(Integer,ForeignKey('gadget.id'))
component_maker = Column(String)
host = relationship("Gadget", backref=backref('components', order_by=id))
class ComponentUsingMetal(Base):
__tablename__ = "component_metal"
id = Column(Integer, primary_key=True)
component_id = Column(Integer, ForeignKey('GadgetComponent.id'))
metal = Column(String)
component = relationship("GadgetComponent", backref=backref('gadgetComponentMetals', order_by=id))
On doing the following query:
session.query(User).join("userGadgets", "gadget", "components","gadgetComponentMetals").filter(ComponentUsingMetal.metal == 'iron') , component_metal gets attached to the query twice giving an error 'table name component_metal specified more than once'.
Any idea what I am doing wrong?
I traced the problem down to the following line in selectable.py:
froms = [f for f in froms if f not in toremove] This line removes the tables which are already covered by joins so that FROM clause doesn't have the same table specified more than once. The line didn't remove component_metal even though toremove had it meaning that I had two different Table objects for the same db table. And then I noticed the import for component_metal's class, ComponentUsingMetal, looked different. The others imports looked like:
from myschema import GadgetComponent
from myschema import Gadget
from python.myschema ComponentUsingMetal
As soon as fixed the import the issue went away.

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