Левое внешнее соединение sqlalchemy ORM -> получить в результате один объект

Я хочу повторить SQL-запрос, который я использовал в pgAdmin, чтобы объединить несколько таблиц и загрузить полученную таблицу в файл CSV. Мне удалось добиться успеха с pgAdmin, и я хочу добиться того же результата с помощью SQLAlchemy, поскольку мое приложение взаимодействует с базой данных через SQLAlchemy.

В pgAdmin я использую такой запрос:

SELECT * FROM visits
LEFT JOIN patients ON visits.patient_id = patients.patient_id
LEFT JOIN [many more left joins here]
WHERE visits.visit_id = 1;

Чтобы сделать то же самое с помощью SQLAlchemy, я попытался использовать следующий код:

def get_all_data_for_visit(self, visit_id: int):
    try:
        stmt = select(Visit, Patient).outerjoin(Patient, and_(Visit.patient_id == Patient.patient_id, Visit.visit_id == visit_id))
        result = self.db.execute(stmt).all()
        if result:
            for row in result:
                print(row.Patient.patient_id, row.Visit.visit_type)
            return result
        else:
            return None
    except OperationalError as e:
        self.show_error_msg()

Однако этот код возвращает объект пациентов и объект посещений, что не является желаемым результатом. Я хочу перебрать объекты и распечатать все их атрибуты (например, row.Visit.visit_type) в CSV. Я не могу просто перебрать все существующие объекты, потому что некоторые из них могут быть None, но мне все равно нужны результаты (в данном случае null) в таблице. С моим SQL-запросом это работает. С sqlalchemy я получаю AttributeError.

Я прочитал связанные вопросы, но не смог найти решение моего конкретного вопроса.

Спасибо за помощь!

Вам необходимо перечислить все таблицы в списке аргументов select(). Если вы перечислите только Visit и Patient, это все равно, что написать SELECT visits.*, patients.* вместо SELECT *.

Barmar 01.05.2024 22:34
Visit.visit_id == visit_id не должно быть в outerJoin() состоянии.
Barmar 01.05.2024 22:35

@Barmar: Сначала я попробовал использовать только «Пациент» и «Посещение», поэтому в списке выбора у меня есть только «Посещение» и «Пациент». Моя проблема в том, что я получаю обратно объект-пациент и объект-посещение. Но мне нужен один объект, в котором все атрибуты (пациента и посещения) объединены (нулевые, если они отсутствуют). Где мне поставить Visit.visit_id == visit_id?

Mika2019 01.05.2024 22:40

Я не использую SQLAlchemy, но не думаю, что это вообще так работает. Каждая таблица соответствует своему классу, поэтому вы получаете экземпляры этого класса. В каком классе вы хотели бы получить желаемый результат?

Barmar 01.05.2024 22:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
78
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Уровень ORM SQLAlchemy возвращает кортежи объектов в результатах соединения, потому что, как правило, программисты используют ORM, потому что с ним удобно работать с графиками объектов, а не с плоскими массивами данных строк. В случае с вопросом - запись результатов запроса на соединение непосредственно в отчет - работать с плоским массивом удобнее. Решение состоит в том, чтобы использовать базовый уровень SQLAlchemy вместо ORM, который будет вести себя как обычный SQL.

Базовый уровень работает с таблицами, а не с объектами ORM. Учитывая, что классы сущностей ORM существуют, таблицы можно получить из них:

patients = Patient.__table__
visits = Visit.__table__

Оператор запроса практически идентичен, за исключением того, что доступ к столбцам осуществляется через атрибут таблицы .c (или .columns), а не напрямую:

q = stmt = (
    sa.select(Patient, Visit)
    .outerjoin(Patient, Visit.patient_id == Patient.patient_id)
    .where(Visit.visit_id == visit_id)
)

Чтобы воспроизвести SQL в вопросе, я изменил порядок таблиц в проекции и, как предполагал Бармар, переместил фильтр visit_id из условия соединения в предложение WHERE. Запрос может быть выполнен посредством сеанса или соединения.

Результатом выполнения будет экземпляр CursorResult, который можно рассматривать как итерацию именованных кортежей. Доступ к отдельным столбцам в строке можно получить по имени или позиции.

Обратите внимание, что SQLAlchemy автоматически устраняет неоднозначность идентификаторов, которые появляются несколько раз в столбцах результатов. Вы можете увидеть имена столбцов в результате, вызвав метод CursorResult.keys().

Другие вопросы по теме