Sqlalchemy и фильтр на отношения

Из приведенного ниже кода я определил отношения следующим образом:

UnitTypeRowModel (self-referencing but only one in the doc)
  -- PlanPmDefinition (definition in the json doc)
    -- PlanPmValues (value in the json doc)

Текущий результат как json, если у меня только один plan_ou в таблице базы данных:

[{
    "children": [],
    "parent_id": 1,
    "id": 4
}, {
    "children": [],
    "parent_id": 2,
    "plan_pm_id": 2,
    "id": 5
    "definition": {
        "id": 2,
        "tag": 0,
        "value": {
            "plan_pm_id": 2,
            "tag": 0,
            "plan_ou": 1
        }
    }
}]

Код:

class PlanPmValues(Base):
    __tablename__ = 'plan_pm_municipal_values'

    plan_pm_id = Column(Integer, primary_key=True)
    tag = Column(Integer, primary_key=True)
    plan_ou = Column(Integer)  # Filter on this
    ...


class PlanPmDefinition(Base):
    __tablename__ = 'plan_pm_municipal_definition'

    id = Column(Integer, primary_key=True)
    tag = Column(Integer, primary_key=True) -- always 0

    value = relationship(PlanPmValues,
               primaryjoin='and_(PlanPmDefinition.id==PlanPmValues.plan_pm_id, ' +
               'PlanPmDefinition.tag==PlanPmValues.tag)',
               foreign_keys='[PlanPmValues.plan_pm_id, PlanPmValues.tag]',
               lazy='joined', uselist=False)


class UnitTypeRowModel(Base):
    __tablename__ = 'unit_type_row_model'
    __table_args__ = {'schema': base_schema}

    id = Column(Integer, primary_key=True)
    client_id = Column(Integer, ForeignKey(base_schema + '.client.id'))
    parent_id = Column(Integer, ForeignKey(base_schema + '.unit_type_row_model.id'), nullable=True)
    plan_pm_id = Column(Integer, nullable=True)

    children = relationship(
             'UnitTypeRowModel',
             lazy='joined',
             join_depth=2,
             order_by="UnitTypeRowModel.sort_order")

    definition = relationship(
             'PlanPmDefinition',
             primaryjoin='and_(PlanPmDefinition.id==UnitTypeRowModel.plan_pm_id, ' +
             'PlanPmDefinition.tag==0)',
             foreign_keys='[UnitTypeRowModel.plan_pm_id]',
             lazy='joined',
             uselist=False)

    @staticmethod
    def get_for_unit(client_id, unit_id):
        db_session = DatabaseEngine.get_session()
        row_models = db_session.query(UnitTypeRowModel).\
            filter(UnitTypeRowModel.client_id == client_id).\
            order_by(UnitTypeRowModel.sort_order)
        json = util.Serialize.serialize_to_json(row_models)
        db_session.close()
        return json

Как мне отфильтровать plan_ou из класса PlanPmValues в методе UnitTypeRowModel.get_for_unit?

1 ответ

Вы должны использовать обычай ColumnPropery за это.

from sqlalchemy import select, and_
from sqlalchemy.orm import column_property

class UnitTypeRowModel(Base):

    ...

    _ou_join = and_(plan_pm_id==PlanPmDefinition.id,
                    PlanPmDefinition.tag==PlanPmValues.tag,
                    plan_pm_id==PlanPmValues.plan_pm_id)

    plan_ou = column_property(select([PlanPmValues.plan_ou],
                                     whereclause=_ou_join).as_scalar())

Я сломал _ou_join как отдельное свойство только для удобства чтения.

То, как вы определили это в своем коде SQLAlchemy, вы имеете отношение многие к одному UnitTypeRowModel а также PlanPmDefinition; и у вас есть отношения многие-к-одному между PlanPmDefinition а также PlanPmValues, Я предполагаю, что из uselist=False аргументы обоих relationship() звонки, и вы можете столкнуться с некоторыми другими ошибками, если вы не применили это в своей базе данных из-за использования уникальных ключей. Это означает, что полученное значение может вернуть только одну строку, которую я принудительно использовал as_scalar(),

Как правило, вы обрабатываете запросы к связанным объектам, как это, используя association_proxy(), Это не идеально в этом случае, потому что у вас уже есть и объект ассоциации (PlanPmDefinition) между UnitTypeRowModel а также PlanPmValues; это означает, что association_proxy позволит вам справиться PlanPmValue объекты непосредственно из UnitTypeRowModel класс, но не plan_ou ценности.

Наконец, вы всегда должны проверять, что вам нужно сделать это как SQLAlchemy Query.filter() - то есть выполнение запросов и фильтрации на сервере SQL - вместо того, чтобы делать это в Python через filter(), Первый занимает больше времени для написания кода и тестирования, но почти всегда будет быстрее, потому что серверы SQL оптимизированы для этого; последний занимает меньше времени для написания кода и на практике вызывает проблемы с производительностью только тогда, когда вы делаете много запросов, которые возвращают много результатов. Лично я пишу вещи на Python, а затем делаю медленные запросы через SQL.

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