В SQLAlchemy ORM, как получить столбцы первичного ключа для использования в session.query (). Group_by ()? - PullRequest
1 голос
/ 06 февраля 2020

Вопрос

Как получить список всех столбцов первичного ключа модельного класса и всех его родителей в иерархии polymorphi c, чтобы я мог использовать его в session.query().group_by?

Подробности

В SQLAlchemy ORM, если я запрашиваю класс, являющийся частью иерархии полиморфизма c, и я хочу GROUP BY это первичный ключ, я также должен GROUP BY все первичные ключи его родителей в иерархии polymorphi c.

Представьте себе следующую настройку, основанную на Иерархиях наследования классов отображения в документации по sqlalchemy:

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    type = Column(String(50))

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type
    }

class Engineer(Employee):
    __tablename__ = 'engineer'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    engineer_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'engineer',
    }

class EngineeringTask(Base):
    __tablename__ = 'engineering_task'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))

    assigned_engineer_id = Column(ForeignKey(Engineer.id))
    assigned_engineer = relationship(Engineer, backref=backref("assigned_tasks", lazy="dynamic"))

Если я хочу выполнить запрос, подобный

session.query(
    Engineer,
).join(
    Engineer.assigned_tasks,
).add_columns(
    func.count(EngineeringTask.id).label('assigned_task_count'),
).group_by(Engineer.id, Employee.id)

Такой запрос, который выбирает все столбцы из Engineer и Employee, но не агрегирует их, возможен в PostgreSQL , потому что (выделено мной):

Когда присутствует GROUP BY (...), выражения списка SELECT недопустимы для ссылки на несгруппированные столбцы, кроме как внутри агрегатных функций или когда несгруппированный столбец является функционально де pendent для сгруппированных столбцов , так как в противном случае было бы более одного возможного значения, которое можно вернуть для не сгруппированного столбца. Функциональная зависимость существует, если сгруппированные столбцы (или их подмножество) являются первичным ключом таблицы, содержащей несгруппированный столбец.

Но для этого необходимо знать / care / запомните все столбцы первичного ключа выбранного класса и все его родители в иерархии полиморфий c (в данном случае Engineer.id и Employee.id)

Как получить список все столбцы первичного ключа Engineer и все родительские элементы в иерархии polymorphi c, динамически?

1 Ответ

0 голосов
/ 14 февраля 2020

Лучшее, что я мог придумать на данный момент - это функция:

from sqlalchemy import inspect

def polymorphic_primary_keys(cls): 
    mapper = inspect(cls)
    yield from (column for column in mapper.columns if column.primary_key) 
    if mapper.inherits is not None: 
        yield from polymorphic_primary_keys(mapper.inherits.entity)

Используйте это так:

query = session.query(
    Engineer,
).join(
    Engineer.assigned_tasks,
).add_columns(
    func.count(EngineeringTask.id).label('assigned_task_count'),
).group_by(*polymorphic_primary_keys(Engineer))

Где inspect(Engineer) возвращает Engineer.__mapper__, что является Mapper, содержащий:

  • Mapper.columns, итератор / словарь / атрибут доступа к столбцам сопоставленной модели
  • Mapper.inherits, который является отображением родительского объекта, если таковой имеется.

Это неудовлетворительно по нескольким причинам:

  • Существует Mapper.primary_key, который предположительно является итератором первичных ключей, но я не могу его использовать, так как он возвращает первичные ключи самого верхнего родителя, а не текущей сопоставленной сущности, поэтому я должен выполнить итерацию свыше Mapper.columns и проверьте атрибут primary_key.

  • Существует метод Mapper.polymorphic_iterator() и атрибут Mapper.self_and_descendants, которые оба вернуть итератор, содержащий текущий преобразователь и преобразователи всех «нисходящих» подклассов текущего объекта. Почему не существует эквивалента для суперклассов?

Но он выполняет свою работу ...

...