Two levels deep one-to-many relationship with contains() - python

I have two tables like so
class Department(Model):
id = Column(Integer, primary_key=True)
employees = relationship('Employee', backref='department')
class Employee(Model):
id = Column(Integer, primary_key=True)
name = Column(String)
department_id = Column(Integer, ForeignKey('department.id'))
email_addresses = relationship('EmailAddress', backref='employee')
class EmailAddress(Model):
id = Column(Integer, primary_key=True)
value = Column(String)
employee_id = Column(Integer, ForeignKey('employee.id'))
And now I need to find all departments that have an employee with an email address of X.
Back when each employee was allowed to have only one email address (so they had email_address = Column(String) I used this code, but I'm not sure how to extend it to work with multiple emails:
session.query(Department).filter(
Employee.email_address==request.args['email']
).outerjoin((Employee, Department.employees))

You can use the any() method of relationship. This code should do it:
session.query(Department).filter(
Employee.email_address.any(value=request.args['email'])
).outerjoin((Employee, Department.employees))

You could use any() like this :
department1 = Department()
department2 = Department()
DBSession.add(department1)
DBSession.add(department2)
employee = Employee()
employee.email_addresses.append(EmailAddress(employee.id, 'e#mail.com'))
DBSession.add(employee)
department1.employees.append(employee)
DBSession.flush()
print(DBSession.query(Department).filter(Department.employees.any(Employee.email_addresses.any(EmailAddress.value == 'e#mail.com'))).all())
see this gist for a full example.

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 to use relationships in SQLAlchemy

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

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]

Optionally reference a single table from multiple other tables

I'm a total beginner to Flask and SQLAlchemy and I'm stuck on what should be quite a simple problem.
My application includes the following two tables:
class Item(db.Model):
id = db.Column(UUIDType(), primary_key=True)
# more columns
class Person(db.Model)
id = db.Column(UUIDType(), primary_key=True)
# more columns
Unnecessary columns have been removed for brevity. I'm required to add a third kind of table, Description, which has a number of properties. Item and Person can optionally have descriptions.
This is what I've done:
class Description(db.Model):
id = db.Column(UUIDType(), primary_key=True)
text = db.Column(db.BLOB, nullable=False)
# some other properties
class Item(db.Model):
id = db.Column(UUIDType(), primary_key=True)
description_id = db.Column(UUIDType(), db.ForeignKey('tags.id'), nullable=True)
description = db.relationship('Description')
class Person(db.Model)
id = db.Column(UUIDType(), primary_key=True)
description_id = db.Column(UUIDType(), db.ForeignKey('tags.id'), nullable=True)
description = db.relationship('Description')
I'm not sure if this is the right way to approach the problem, particularly since the description_id column is nullable but the description relationship is not.
Is this correct? And if so (doubtful), is there a better way to solve the problem?

Inserting new records with one-to-many relationship in sqlalchemy

I'm following the flask-sqlalchemy tutorial on declaring models regarding one-to-many relationship. The example code is as follows:
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
addresses = db.relationship('Address', backref='person',
lazy='dynamic')
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50))
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
Now I'm wondering how to insert new records into DB using such model. I assume I need a constructor init, but I have difficulties to understand how it should be implemented and used. The main problem for me here is that Person depends on Address and Address has ForeignKey to Person, so it should know about the Person in advance.
Plase help me to understand how it should be performed.
Thank you in advance.
You dont need to write a constructor, you can either treat the addresses property on a Person instance as a list:
a = Address(email='foo#bar.com')
p = Person(name='foo')
p.addresses.append(a)
Or you can pass a list of addresses to the Person constructor
a = Address(email='foo#bar.com')
p = Person(name='foo', addresses=[a])
In either case you can then access the addresses on your Person instance like so:
db.session.add(p)
db.session.add(a)
db.session.commit()
print(p.addresses.count()) # 1
print(p.addresses[0]) # <Address object at 0x10c098ed0>
print(p.addresses.filter_by(email='foo#bar.com').count()) # 1
I've gathered information here and elsewhere and found 3 ways to do so. In this model example (same as question):
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
addresses = db.relationship('Address', backref='person',
lazy='dynamic')
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50))
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
1.
a = Address(email='foo#bar.com')
p = Person(name='foo', addresses=[a])
2.
p = Person(name='foo')
a = Address(email='foo#bar.com', person_id=p.id)
3.
a = Address(email='foo#bar.com')
p = Person(name='foo')
p.addresses.append(a)
The most important thing while looking into this model is to understand the fact that this model has a one to many relationship, i.e. one Person has more than one address and we will store those addresses in a list in our case.
So, the Person class with its init will look something like this.
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
addresses = db.relationship('Address', backref='person',
lazy='dynamic')
def __init__(self,id,name,addresses = tuple()):
self.id = id
self.name = name
self.addresses = addresses
So this Person class will be expecting an id, a name and a list that contains objects of type Address. I have kept that the default value to be an empty list.
Hope it helps. :)
Additionally to all previous answers, in one-to-one relationships with uselist=False, like:
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
address = db.relationship('Address', backref='person',
lazy=True, uselist=False)
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50))
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
Only next approach helped to insert records:
p = Person(name='foo')
a = Address(email='foo#bar.com', person=p) # Adding created person directly to Address's backref

Categories

Resources