Reputation: 3857
I’ve almost figured out how to translate this query into SQLAlchemy
:
select u.* from (select
unit_id,
activity,
max(occurred_at) maxOA
from Activity
group by unit_id) a1
inner join Activity a2 on
a2.unit_id = a1.unit_id and a2.occurred_at = a1.maxOA
inner join Unit u on
u.id = a2.unit_id where a2.activity = 'activateReq';
My tables look like this (gDB is the global variable that holds my SQLAlchemy DB connection):
class Unit(gDB.Model):
__tablename__ = 'Unit'
id = gDB.Column('id', gDB.Integer(), primary_key=True)
owner_id = gDB.Column('owner_id', gDB.ForeignKey('User.id'))
owner = gDB.relationship(User, primaryjoin=owner_id == User.id)
unitNumber = gDB.Column('unit_number', gDB.Integer())
street = gDB.Column('street', gDB.String(255))
city = gDB.Column('city', gDB.String(255))
state = gDB.Column('state', gDB.String(2))
zip = gDB.Column('zip', gDB.String(9))
serviceInstallDate = gDB.Column('service_install_date', gDB.DateTime())
activity = gDB.relationship('Activity', backref='unit_id', lazy='select', order_by="desc(Activity.occurredAt)")
def __repr__(self):
return '<Unit: {0} {1}>'.format(self.unitNumber, self.street)
class Activity(gDB.Model):
__tablename__ = 'Activity'
id = gDB.Column('id', gDB.Integer(), primary_key=True)
unitID = gDB.Column('unit_id', gDB.ForeignKey('Unit.id'))
unit = gDB.relationship(Unit, primaryjoin=unitID == Unit.id)
agentID = gDB.Column('agent_id', gDB.ForeignKey('User.id'))
agent = gDB.relationship(User, primaryjoin=agentID == User.id)
occurredAt = gDB.Column('occurred_at', gDB.DateTime())
activity = gDB.Column('activity', gDB.String(32))
notes = gDB.Column('notes', gDB.String())
def __repr__(self):
return '<Activity: {0} {1}>'.format(self.activity, self.occurredAt)
I’ve gotten this far:
db.session.query(Activity.unitID, Activity.activity, func.max(Activity.occurredAt).label("maxOA"))
.group_by(Activity.unitID)
.subquery()
a2 = expression.alias(Activity)
u = expression.alias(Unit)
q = db.session.query(a1)
.join(a2, and_(a2.c.unit_id == a1.c.unit_id, a2.c.occurred_at == a1.c.maxOA))
.join(u, u.c.id == a2.c.unit_id).filter(a2.c.activity == 'activateReq')
q.all()
q.all()
is returning the right set of rows, but I want the columns of Unit
, not of the subquery a1
.
Update
I’ve gotten closer: The trick is to query()
the desired columns (in this case, all the columns of u
), and then select_from()
the subquery a1
:
q = db.session.query(u).select_from(a1).join(a2, and_(a2.c.unit_id == a1.c.unit_id, a2.c.occurred_at == a1.c.maxOA)).join(u, u.c.id == a2.c.unit_id).filter(a2.c.activity=='activateReq')
The problem now is that the resulting Unit
instances don’t have their related User
(owner_id
/owner
) objects; my Jinja template tries to reference the user.owner and I get
'sqlalchemy.util._collections.result object' has no attribute 'owner'
I also have no idea if this is the most concise or performant way to do this.
Upvotes: 0
Views: 2674
Reputation: 3857
Ah! I figured it out. I'm using expression.alias()
instead of orm.aliased()
. It should be like this:
a1 = gDB.session.query(Activity.unitID, Activity.activity, func.max(Activity.occurredAt).label("maxOA")) \
.group_by(Activity.unitID) \
.subquery()
a2 = aliased(Activity, name="a2")
u = aliased(Unit, name="u")
q = gDB.session.query(u).select_from(a1) \
.join(a2, and_(a2.unitID == a1.c.unit_id, a2.occurredAt == a1.c.maxOA)) \
.join(u, u.id == a2.unitID).filter(a2.activity == 'activateReq')
Upvotes: 2