SQLAlchemy get parent object when using group_by and join - sqlalchemy

I'm learning SQLAlchemy on a new project and I'm struggling how to get "parent" object when using group_by on a join table.
class Address(Base):
__tablename__ = "addresses"
id_address = Column(Integer, primary_key=True)
country = Column(String)
[...]
class Order(Base):
__tablename__ = "orders"
id_order = Column(Integer, primary_key=True)
id_billing = Column(Integer, ForeignKey("addresses.id_address"))
id_shipping = Column(Integer, ForeignKey("addresses.id_address"))
billing = relationship(Address, foreign_keys=id_billing)
shipping = relationship(Address, foreign_keys=id_shipping)
[...]
billing = aliased(models.Address, name="billing")
shipping = aliased(models.Address, name="shipping")
db.query(shipping.country) \
.join(billing, models.Order.billing) \
.join(shipping, models.Order.shipping) \
.group_by(shipping.country)
Returns a list, like:
[('DE',), ('ES',), ('GR',), ('HU',), ('LT',), ('LV',), ('RO',)]
What I would like is a list of models.Order objects containing only "models.Order.shipping.country".
Any hints or working examples are very appreciated!

Related

how to fetch data from multiple tables without doing join in sqlalchemy (Using fastApi)

class Actions(Base):
__tablename__ = "actions"
id = Column(Integer, primary_key=True, index=True)
resource_id = Column(Integer, ForeignKey('resources.id'))
action_short_name = Column(String(20),nullable=True)
description = Column(String(250),nullable=True)
status = Column(String(1),nullable=True)
action_resource = relation(Resource, backref = "actions")
detailsQ = session.query(Actions).join(Resource).filter(Actions.resource_id == Resource.id).all()
I don't want to use join so how I am able to fetch data though I have made relations ?

NotNullViolation when appending to relationship using an Association Object in SQLAlchemy?

Background
The documentation gives the following example of a parent-child-association being added by appending that association to p.children. The child is then accessed via p.children and I assume the other way is possible as well, i.e accessing the parent via c.parents.
class Association(Base):
__tablename__ = 'association'
left_id = Column(ForeignKey('left.id'), primary_key=True)
right_id = Column(ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", back_populates="parents")
parent = relationship("Parent", back_populates="children")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association", back_populates="parent")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Association", back_populates="child")
# create parent, append a child via association
p = Parent()
a = Association(extra_data="some data")
a.child = Child()
p.children.append(a)
# iterate through child objects via association, including association
# attributes
for assoc in p.children:
print(assoc.extra_data)
print(assoc.child)
Problem
I have three classes that are almost identical to those in the example:
class TopicSubcription(Base):
__tablename__ = 'topic_subscription'
topic_id = Column(ForeignKey('topic.id'), primary_key=True)
user_id = Column(ForeignKey('user.id'), primary_key=True)
subscription_date = Column('subscription_date', DateTime, nullable=True)
topic = relationship("Topic", back_populates="subscribed_users")
user = relationship("User", back_populates="followed_topics")
class Topic(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
subscribed_users = relationship("Association", back_populates="topic")
class User(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
followed_topics = relationship("Association", back_populates="user")
but when I run the following I get a NotNullViolation error stating that user_id violates not-null constaint.
u: User = session.query(User).filter(User.id == 'example_id1').one()
t: Topic = session.query(Topic).filter(Topic.id == 'example_id2').one()
sub = TopicSubscription(func.now())
sub.topic = t
u.followed_topics.append(sub)
session.add(u)
session.commit()
Question
Am I misinterpreting the documentation or is it wrong?
I had something quite similar just now and was going crazy trying to work it out. Abstracting to the parent/child example from the docs, what worked in my case was:
p = Parent()
a = Association(parent=p, extra_data="some data")
a.child = Child()
p.children.append(a)

Confused with multiple Join in single query, Sqlalchemy

I am trying to join three tables in a single query in SQLAlchemy specifically but unable to do so.
I have done this so far :-
Test.query.join(Test.sections).join(QuestionSection.section).all()
I know it is wrong but I am trying to do it in a single query.
Here are my tables :-
Question section Model :-
class QuestionSection(Dictifiable, db.Model):
__tablename__ = 'question_section'
section_id = Column(Integer, ForeignKey('section.id'), primary_key=True)
question_id = Column(Integer, ForeignKey('question.id'), primary_key=True)
question = db.relationship('Question', backref='question_section')
section = db.relationship('Section', backref='question_section')
Section Table :-
class Section(Dictifiable, db.Model):
__tablename__ = 'section'
id = Column(Integer, Sequence('section_id_seq'), primary_key=True)
test_id = Column(Integer, ForeignKey('test.id'))
name = Column(String(50))
test = relationship("Test", back_populates="sections")
questions = db.relationship('Question',
secondary='question_section')
And Test table :-
id = Column(Integer, Sequence('test_id_seq'), primary_key=True)
name = Column(String(50))
sections = relationship("Section", back_populates="test")
tests = relationship("Corporate", secondary='corporate_test',
back_populates='tests')
I understood what I was doing wrong, I should join like this :-
Test.query.join(Test.sections).join(QuestionSection, QuestionSection.section_id == Section.id).all()

How to query a relationship on multiple polymorphic-inheritance tables?

Let's say you have the following simplified example schema, which uses SQLAlchemy joined table polymorphic inheritance. Engineer and Analyst models have a Role relationship. The Intern model does not.
class Role(db.Model):
__tablename__ = 'role'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(16), index=True)
class EmployeeBase(db.Model):
__tablename__ = 'employee_base'
id = db.Column(db.Integer, primary_key=True)
some_attr = db.Column(db.String(16))
another_attr = db.Column(db.String(16))
type = db.Column(db.String(50), index=True)
__mapper_args__ = {
'polymorphic_identity': 'employee',
'polymorphic_on': type
}
class Engineer(EmployeeBase):
__tablename__ = 'engineer'
id = db.Column(db.Integer, db.ForeignKey('employee_base.id'), primary_key=True)
role_id = db.Column(db.Integer, db.ForeignKey('role.id'), index=True)
role = db.relationship('Role', backref='engineers')
__mapper_args__ = {
'polymorphic_identity': 'engineer',
}
class Analyst(EmployeeBase):
__tablename__ = 'analyst'
id = db.Column(db.Integer, db.ForeignKey('employee_base.id'), primary_key=True)
role_id = db.Column(db.Integer, db.ForeignKey('role.id'), index=True)
role = db.relationship('Role', backref='analysts')
__mapper_args__ = {
'polymorphic_identity': 'analyst',
}
class Intern(EmployeeBase):
__tablename__ = 'intern'
id = db.Column(db.Integer, db.ForeignKey('employee_base.id'), primary_key=True)
term_ends = db.Column(db.DateTime, index=True, nullable=False)
__mapper_args__ = {
'polymorphic_identity': 'intern',
}
If I want to find Employees with a Role name having "petroleum" somewhere in the name, how would I do that?
I've tried many, many approaches. The closest I've come is this, which only returns Analyst matches:
employee_role_join = with_polymorphic(EmployeeBase,
[Engineer, Analyst])
results = db.session.query(employee_role_join).join(Role).filter(Role.name.ilike('%petroleum%'))
If I try to do something like this, I get an AttributeError, because I'm searching on an attribute of the joined Role table:
employee_role_join = with_polymorphic(EmployeeBase,
[Engineer, Analyst])
results = db.session.query(employee_role_join).filter(or_(
Engineer.role.name.ilike('%petroleum%'),
Analyst.role.name.ilike('%petroleum%')))
You can try specifying the join ON clause explicitly since the issue with your first query seems to be that Role is joining only on the analyst.role_id column:
employee_role_join = with_polymorphic(EmployeeBase, [Engineer, Analyst])
results = session.query(employee_role_join).join(Role).filter(Role.name.ilike('%petroleum%'))
print(str(results))
SELECT employee_base.id AS employee_base_id,
employee_base.some_attr AS employee_base_some_attr,
employee_base.another_attr AS employee_base_another_attr,
employee_base.type AS employee_base_type,
engineer.id AS engineer_id,
engineer.role_id AS engineer_role_id,
analyst.id AS analyst_id,
analyst.role_id AS analyst_role_id
FROM employee_base
LEFT OUTER JOIN engineer ON employee_base.id = engineer.id
LEFT OUTER JOIN analyst ON employee_base.id = analyst.id
JOIN role ON role.id = analyst.role_id
WHERE lower(role.name) LIKE lower(?)
employee_role_join is an AliasedClass that exposes both Analyst and Engineer, which we can then use to create a join-ON clause like so:
results = session.query(employee_role_join)\
.join(Role, or_( \
employee_role_join.Engineer.role_id==Role.id, \
employee_role_join.Analyst.role_id==Role.id \
))\
.filter(Role.name.ilike('%petroleum%'))
which changes the resulting SQL to JOIN role ON engineer.role_id = role.id OR analyst.role_id = role.id
Define the role_id on EmployeeBase. Even though Intern doesn't have the relationship back to the role table, the field can be null for that case.
I changed EmployeeBase to this:
class EmployeeBase(db.Model):
__tablename__ = 'employee_base'
id = db.Column(db.Integer, primary_key=True)
role_id = db.Column(db.Integer, db.ForeignKey('role.id'), index=True)
given_name = db.Column(db.String(16))
surname = db.Column(db.String(16))
type = db.Column(db.String(50), index=True)
__mapper_args__ = {
'polymorphic_identity': 'employee',
'polymorphic_on': type
}
And removed the role_id column definition from all other employee models.
db.create_all()
petrolium_engineer = Role(name='Petrolium Engineer')
geotech_engineer = Role(name='Geotech Engineer')
analyst_petrolium = Role(name='Analyst of Petrolium')
db.session.add(petrolium_engineer)
db.session.add(geotech_engineer)
db.session.add(analyst_petrolium)
db.session.add(
Intern(given_name='Joe', surname='Blogs', term_ends=datetime.now())
)
db.session.add(
Engineer(given_name='Mark', surname='Fume', role=petrolium_engineer)
)
db.session.add(
Engineer(given_name='Steve', surname='Rocks', role=geotech_engineer)
)
db.session.add(
Analyst(given_name='Cindy', surname='Booker', role=analyst_petrolium)
)
db.session.commit()
petrolium_roles = db.session.query(EmployeeBase).join(Role).\
filter(Role.name.contains('Petrolium')).all()
for emp in petrolium_roles:
print(f'{emp.given_name} {emp.surname} is {emp.role.name}')
# Mark Fume is Petrolium Engineer
# Cindy Booker is Analyst of Petrolium

Alembic Migration: How to remove polymorphic Identity

I have the following setup
class Content(Base):
"""Content object"""
__tablename__ = "content"
id = Column(Integer, primary_key=True)
name = Column(Unicode(255),unique=True, nullable=False)
title = Column(Unicode(255))
body = Column(UnicodeText)
created = Column(DateTime, default=func.now())
modified = Column(DateTime, onupdate=func.now())
type = Column(String(20))
__mapper_args__ = {
'polymorphic_on':type,
'polymorphic_identity':'content',
'with_polymorphic':'*'
}
class Locality(Content):
__tablename__ = "local"
id = Column(Integer, ForeignKey('content.id'),primary_key=True)
city_name = Column(Unicode(80))
__mapper_args__ = {'polymorphic_identity':'city'}
Now I dropped the Locality table using alembic.
Each time I query Content, I get
AssertionError: No such polymorphic_identity 'city' is defined
How do I drop this polymorphic_identity
I got over this by applying MySQL 'delete from' command on the content through MySQL Console, finding those contents whose type is 'city'
delete from content where content.type='city';