'How to refer to a column that is the result of an UNION in sqlalchemy?
I have two tables that I'm extracting names from
animal_names = session.query(Animal.name)
human_names = session.query(Human.name)
I want the union of these two
all_names = animal_names.union(human_names)
So that I can join it with another table
query = all_names.join(NameDirectory, NameDirectory.name == <SOMETHING>)
How do I express the right side of the join condition since I don't have a column name for the union?
Looking online I was hoping this would work:
animal_names = session.query(Animal.name).label("name")
human_names = session.query(Human.name).label("name")
all_names = animal_names.union(human_names)
query = all_names.join(NameDirectory, NameDirectory.name == all_names.c.name)
But I get AttributeError: 'Query' object has no attribute 'c'. Is there a way to reference the first column of a Query object to use it in conditions like for JOIN or or WHERE?
Solution 1:[1]
I think this might be a problem better solved by SQLAlchemy 1.4/2.0 select() style queries. I was able to rearrange the join and use .subquery() but I think it might be better to use select(). There is a discussion from 2020 about the issues with unions that seems like it might still apply.
Code
from sqlalchemy import (
create_engine,
Integer,
String,
)
from sqlalchemy.schema import (
Column,
)
from sqlalchemy.sql import select, union
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import Session
Base = declarative_base()
engine = create_engine("sqlite://", echo=False)
class Animal(Base):
__tablename__ = 'animals'
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
class Human(Base):
__tablename__ = 'humans'
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
class NameDirectory(Base):
__tablename__ = 'name_directories'
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
Base.metadata.create_all(engine)
with Session(engine) as session:
dog = Animal(name='Spot')
human = Human(name='Guy')
human2 = Human(name='Man')
session.add_all([dog, human, human2])
for obj in (dog, human):
session.add(NameDirectory(name=obj.name))
session.commit()
with Session(engine) as session:
all_names = session.query(Animal.name.label("name")).union(session.query(Human.name.label("name"))).subquery('all_names')
query = session.query(NameDirectory.name).join(all_names, all_names.c.name == NameDirectory.name)
print(query)
print(query.all())
with Session(engine) as session:
q1 = select([Animal.name.label('name')]).select_from(Animal)
q2 = select([Human.name.label('name')]).select_from(Human)
sub = union(q1, q2).alias()
q = select([sub.c.name]).select_from(sub.join(NameDirectory, sub.c.name == NameDirectory.name))
print(q)
print(session.execute(q).all())
Output
SELECT name_directories.name AS name_directories_name
FROM name_directories JOIN (SELECT anon_1.name AS name
FROM (SELECT animals.name AS name
FROM animals UNION SELECT humans.name AS name
FROM humans) AS anon_1) AS all_names ON all_names.name = name_directories.name
[('Spot',), ('Guy',)]
SELECT anon_1.name
FROM (SELECT animals.name AS name
FROM animals UNION SELECT humans.name AS name
FROM humans) AS anon_1 JOIN name_directories ON anon_1.name = name_directories.name
[('Spot',), ('Guy',)]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ian Wilson |
