recommended way to eagerly load object relationship retrieved via complex query ? #6126
-
Hi there, After going through the documentation for a while, I was wondering what would be the best/recommended way to construct an eagerly loaded relationship that would involve a rather difficult query/subquery Let's say I have the following situation: a task which have multiple task updates and I want to create a joined relationship on the task object to always have the first task update loaded along with the task. import datetime
from datetime import timedelta
import pytz
from sqlalchemy import create_engine, Column, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.types import DateTime, Integer, String
engine = create_engine("sqlite://", echo=True)
Base = declarative_base()
def timestamp_utc_with_timezone():
dt = datetime.datetime.utcnow()
dt = dt.replace(tzinfo=pytz.utc)
return dt
class Task(Base):
__tablename__ = "task"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False)
updates = relationship("TaskUpdate", passive_deletes="all")
# first_update = relationship("TaskUpdate", lazy="joined") # HERE
def __repr__(self):
return f"Task(id={self.id})"
class TaskUpdate(Base):
__tablename__ = "task_update"
id = Column(Integer, primary_key=True)
task_id = Column(Integer, ForeignKey("task.id", ondelete="CASCADE"))
timestamp = Column(DateTime(timezone=True), default=timestamp_utc_with_timezone)
info = Column(String)
def __repr__(self):
return f"TaskUpdate(task_id={self.task_id}, timestamp={self.timestamp.isoformat()})"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
task = Task(name="Task 1")
session.add(task)
for i in range(5):
st = TaskUpdate(info=f"info:{i}", timestamp=timestamp_utc_with_timezone() - timedelta(hours=1))
task.updates.append(st)
session.commit()
task = session.query(Task).first()
# print(task.first_update) in SQL -- example:
SELECT task.*, task_update.*
FROM (
SELECT task_1.*,
(SELECT task_update.id
FROM task_update
where task_update.task_id = task_1.id
ORDER BY timestamp
LIMIT 1) first_task_update_id
FROM task As task_1
where task_1.id = 1
) task
JOIN task_update as task_update ON task_update.id = task.first_task_update_id; Is this even a use case for a relationship or too complex ? Thanks in advance for any advice. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
Hi - you can map to that limit query using the "relationship to aliased class" feature, then make a relationship to that. If your database supports window functions (all the major DBs do now), you can use the recipe at https://docs.sqlalchemy.org/en/14/orm/join_conditions.html#row-limited-relationships-with-window-functions directly which will allow all kinds of eager loading such as "selectinload" to work as expected. |
Beta Was this translation helpful? Give feedback.
Hi -
you can map to that limit query using the "relationship to aliased class" feature, then make a relationship to that. If your database supports window functions (all the major DBs do now), you can use the recipe at https://docs.sqlalchemy.org/en/14/orm/join_conditions.html#row-limited-relationships-with-window-functions directly which will allow all kinds of eager loading such as "selectinload" to work as expected.