Есть ли способ извлечь подмножество элементов из отношения в SqlAlchemy, но сохранить порядок, который я определил в backref
(отношение) при создании класса?
Допустим, у меня есть две таблицы / модели: A User
и UserLog
, которые содержат действия, выполняемые пользователем (Base
- это просто Declarative_base () )
Каждый UserLog
имеет ForeignKey для идентификатора User
, который выполнил действие, и тип действия для каждого журнала можно выбрать из Enum
. Каждый UserLog также имеет отметку времени created
, которую я заказываю с: В обычном сценарии я хотел бы получить журналы от самого старого до самого нового (сначала самого старого).
Однако я также хотел бы иметь возможность получить конкретного пользователя с загруженным только подмножеством журналов (журналов определенного действия). Я понимаю, что это противоречит «сути» объединенной нагрузки, поскольку я получал бы нереалистичное состояние базы данных (есть больше журналов, связанных с пользователем, которые отражал бы мой извлеченный объект User
), но это было бы полезно в некоторых случаях.
class UserLog(Base):
__tablename__ = 'user_logs'
id = Column(UUID(as_uuid=True), primary_key=True)
timestamp_created = Column(
DateTime, server_default=sqlalchemy.text("now()")
)
user_id = Column(
UUID(as_uuid=True),
ForeignKey("users.id", ondelete="CASCADE")
)
action = Column(SaEnum(UserLogsConstants), nullable=False)
user = relationship(
"User",
backref=backref(
'logs', order_by="asc(UserLog.timestamp_created)"
) # The order_by is important here
)
Спасибо за этот SO-ответ Мне удалось загрузить подмножество журналов, используя contains_eager
, но затем я теряю порядок на timestamp_created
, который я указал в backref
.
Допустим, у меня есть два доступных действия для журналов: UPLOAD_START
, UPLOAD_END
(для столбца action
)
Я настроил тест, в котором я создаю User
с 5 UPLOAD_START
действиями и 5 UPLOAD_END
действиями, созданными из строя (я искусственно вставив сначала "новые" действия).
Итак, мне бы хотелось получить User
с его отношением .logs
, содержащим только журналов, события которых UPLOAD_START
и сохраняют порядок по метке времени, указанной в backref
объект.
Вот тестовый запрос
u = session.query(User) \
.filter(User.id == test_user_id) \
.join(User.logs) \
.filter(UserLog.action == UserLogsConstants.UPLOAD_START) \
.options(contains_eager(User.logs)) \
.one()
Эти два утверждения работают нормально:
assert len(u.logs) == 5
# I inserted 10 logs total, only 5 with action=UPLOAD_START
assert all(ul.action == UserLogsConstants.UPLOAD_START for ul in u.logs)
# Neat, only logs with `UPLOAD_START`
Но этот провал: порядок не соблюдается.
assert all(
u.logs[i].timestamp_created < u.logs[i + 1].timestamp_created
for i in range(len(u.logs) - 1)
)
Это имеет смысл. Если я понимаю поведение contains_eager
, это как бы забыть, что вы думаете об отношениях, и использовать то, что я вам говорю в предыдущем запросе (и я ничего не говорю о порядок в этом запросе). И еще: я смотрю на SQL, сгенерированный SqlAlchemy, и здесь нет условия ORDER BY
. Полагаю, я всегда мог бы сам добавить это в запрос, но если бы он был чище, если бы мне это не нужно.
Для тех, кто знаком с Django, я пытаюсь эмулировать объект Prefech
, где я могу указать подзапрос для получения загруженных объектов:
User.objects.prefetch_related(
Prefetch(
'logs',
queryset=UserLogs.objects.filter(action=UserLogsConstants.UPLOAD_START)
)
).get(pk='foo-uuid')