SQLAlchemy: сопоставить результаты CTE/group_by с объектом ORMPython

Программы на Python
Anonymous
SQLAlchemy: сопоставить результаты CTE/group_by с объектом ORM

Сообщение Anonymous »

Проблема:
У меня есть сложный запрос, в котором в определенный момент, если установлен флаг, мне в конечном итоге нужно выполнить group_by() и например. sum() и group_concat(). Моя проблема в том, что после этого мне не удается сопоставить результат с объектом ORM.
Обратите внимание, что сам запрос, похоже, дает правильные результаты; не получается просто сопоставление.
Тот факт, что запрос является «сложным», может накладывать некоторые ограничения на реализацию группировки:
Мне нужно реализовать что-то, что может работать с динамически определенным объектом (под)запроса и создавать что-то, к чему позже можно будет динамически добавлять соединения, фильтры и т. д.; поэтому мне нужно вставить группировку между «шагом разнесения» и «шагом объединения» (см. ниже).
Контекст:
У меня есть таблица, содержащая информацию о том, «какие предметы необходимы для создания (составного) предмета». например
  • планшет состоит из экрана, пластикового корпуса и электронной платы.
  • электронная плата представляет собой состоит из печатной платы, резисторов и т. д.
Так что таблица действительно представляет собой дерево с корнем (таблетка), промежуточными узлами (электронная плата ) и листочки (экран или плата).
(На самом деле в таблице есть рецепты построения нескольких корневых элементов, так что это не ОДНО дерево, но я думаю, здесь дело не в этом) .
Мне нужно поддерживать несколько вариантов представления результатов, т. е. запрос собирается динамически следующим образом:
  • Шаг разнесения: можно выбрать только то, что непосредственно составляет объект (т.е. 1-й уровень элементов, связанных с данным объектом), или, альтернативно, конечные узлы дерева. Например, планшет можно рассматривать как состоящий из экрана, корпуса и электронной платы, или, альтернативно, он может нас не интересовать, зная, что существует промежуточный объект, называемый «электронной платой», поэтому планшет состоит из экрана, шасси, печатная плата, резисторы и т. д. Этот сценарий реализуется путем выполнения рекурсивного CTE.
  • Шаг развертывания: его можно сгруппировать для компонентов. Например, если электронная плата содержит два одинаковых резистора, их можно сгруппировать в одном ряду. Это следует за предыдущим пунктом (т.е. может быть сделано в обоих случаях, либо при взрыве, либо нет). Здесь я застрял.
  • Шаг объединения: результат затем объединяется с несколькими таблицами (товары имеют одного или нескольких производителей, и для у каждого производителя есть один или несколько поставщиков и так далее).
  • Шаг фильтрации/заказа: фильтры/порядок можно применять практически к любому из предыдущие поля, включая те, которые получены из sum(), например, в объединенных таблицах.
Отдельно для «шага взрыва» все реализовано и, похоже, работает нормально.
Код/попытки:
Я пытался используйте .label(), чтобы результаты запроса были полностью совместимы с исходным определением таблицы (которое имеет рабочую карту), то есть с теми же именами столбцов, а затем я использовал aliased(OrmEntity, my_subquery) или select_entity_from( ), но результирующий объект не заполнен правильными значениями; Я получил правильное количество строк, но содержимое элементов просто неправильное и, похоже, не получено из sum() и group_concat().
Я также пытался настроить новую карту для моего запроса (на самом деле CTE) для нового объекта ORM, вместо того, чтобы заставить работать карту, которая у меня уже есть, с исходной таблицей, но я не нашел подходящего способа .
Код:
Схема таблицы:

Код: Выделить всё

bom = Table('bom_details', Base.metadata,
Column('id', Integer(), primary_key=True, autoincrement=True),
Column('bd_bh_mag_id', Integer()),
Column('bd_bh_revisione', String()),
Column('bd_mag_id', Integer()),
Column('bd_mag_id_revisione', String()),
Column('bd_topografia', String()),
Column('bd_flag_non_montare', Integer()),
Column('bd_progressivo', Integer()),
Column('bd_descrizione', String()),
Column('bd_note', String()),
Column('bd_qta', String()),
ForeignKeyConstraint(['bd_bh_mag_id', 'bd_bh_revisione', 'bd_mag_id', 'bd_mag_id_revisione'],
['anamag.mag_id', 'anamag.mag_code_revision', 'anamag.mag_id', 'anamag.mag_code_revision'])

)

Сопоставление выполняется с помощью этого кода:

Код: Выделить всё

mapper_registry.map_imperatively(
BomItem,
bom,
properties={
"descrizione"  : column_property(bom.c.bd_descrizione),
"nm": column_property(bom.c.bd_flag_non_montare),
"index": column_property(bom.c.bd_progressivo),
"refdes": column_property(bom.c.bd_topografia),
"note": column_property(bom.c.bd_note),
"qty": column_property(bom.c.bd_qta),
"parent_anamag": relationship("AnamagItem", lazy='joined',
primaryjoin=(bom.c.bd_bh_mag_id == AnamagItem.iit_code) &
(bom.c.bd_bh_revisione == AnamagItem.revision)),
"anamag": relationship("AnamagItem", lazy='joined',
primaryjoin=(bom.c.bd_mag_id == AnamagItem.iit_code) &
(bom.c.bd_mag_id_revisione == AnamagItem.revision)),
},
)
А «шаг взрыва», который я пытаюсь реализовать, в настоящее время выглядит так (некоторые поля только что исправлены с помощью литералов, но они могут измениться).

Код: Выделить всё

def _query_imploded(self, q):
q = q.subquery()

subq = self.db.session.query(
literal('0').label("bd_bh_mag_id"),
literal('0').label("bd_bh_revisione"),
q.c.bd_mag_id,
q.c.bd_mag_id_revisione,
func.group_concat(q.c.bd_topografia, separator=', ').label("bd_topografia"),
func.group_concat(q.c.bd_flag_non_montare, separator=', ').label("bd_flag_non_montare"),
func.group_concat(q.c.bd_progressivo, separator=', ').label("bd_progressivo"),
literal('').label("bd_descrizione"),
func.group_concat(q.c.bd_note, separator=', ').label("bd_note"),
func.sum(q.c.bd_qta).label("bd_qta")
).group_by(q.c.bd_mag_id, q.c.bd_mag_id_revisione).cte("implode_cte")

#res = self.db.session.query(aliased(self.db.BomItem, subq))
#res = self.db.session.query(self.db.BomItem).select_entity_from(subq)

где q — это либо session.query(BomItem), либо рекурсивный CTE в зависимости от «шага разнесения», а res будет передано в «шаг объединения», который в основном выполняет несколько .outerjoin (и связанных с ним contains_eager) на q, затем упорядочивает, фильтрует и т. д.

Подробнее здесь: https://stackoverflow.com/questions/787 ... orm-entity

Вернуться в «Python»