【问题标题】:Sqlalchemy and filter on relationshipSqlalchemy 和过滤关系
【发布时间】:2014-03-09 12:26:11
【问题描述】:

从下面的代码中,我定义了这样的关系:

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

如果我在数据库表中只有一个plan_ou,则当前结果为json:

[{
    "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

如何在方法UnitTypeRowModel.get_for_unit 中从PlanPmValues 类中过滤plan_ou

【问题讨论】:

    标签: python filter sqlalchemy relationship


    【解决方案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 代码中定义的方式,UnitTypeRowModelPlanPmDefinition 之间存在多对一关系; PlanPmDefinitionPlanPmValues 之间存在多对一的关系。我从uselist=False 参数中推断出这两个relationship() 调用,如果您没有通过使用唯一键在数据库中强制执行,您可能会遇到一些其他错误。这意味着结果值只能返回一行,我已使用 as_scalar() 强制执行。

    一般来说,您可以使用association_proxy() 处理对此类相关对象的查询。在这种情况下,这并不理想,因为您已经在 UnitTypeRowModelPlanPmValues 之间拥有和关联对象 (PlanPmDefinition);这意味着association_proxy 将允许您直接从UnitTypeRowModel 类而不是plan_ou 值处理PlanPmValue 对象。

    最后,您应该始终确认您需要以 SQLAlchemy Query.filter() 的身份执行此操作——也就是说,在 SQL 服务器上执行查询和过滤——而不是在 Python 中执行通过filter()。前者需要更多时间进行编码和测试,但几乎总是更快,因为 SQL 服务器已针对它进行了优化;后者需要更少的时间来编写代码,并且实际上只会在您执行大量返回大量结果的查询时导致性能问题。我个人用 Python 写东西,然后通过 SQL 进行慢速查询。

    【讨论】:

    • 实际上应该是一对一的。我应该忽略tag 或将其添加到连接中,因为它实际上始终为 0 且未使用(虽然不是我做的)。我已将问题更新为不使用多对一(我希望)。我也不想要一个子查询,而是一个直接的左外连接,其中 plan_ou 必须设置在连接子句上,而不是底部位置。我再看看association_proxy
    • 要获得没有子查询的直接连接,您可以将 UnitTypeRowModel 映射到 3 表连接。每次查询 UnitTypeRowModel 时,您已经在执行该连接,事实上,这是因为您的关系中有 lazy=joined 关键字。如果您想从UnitTypeRowModel 类强制执行只读访问,我认为您必须通过python 属性运行它们以防止轻松写入。如果您想查看其中任何一个的代码,请告诉我,我会更新答案。
    • 是的,我想要的都得到了,但我想要的是在查询时从 PlanPmValues-join 中添加一个过滤器。
    猜你喜欢
    • 2021-05-07
    • 2012-01-23
    • 1970-01-01
    • 2018-02-11
    • 1970-01-01
    • 2021-12-17
    • 2021-04-23
    • 2014-03-12
    相关资源
    最近更新 更多