繁体   English   中英

Python/SqlAlchemy 将表连接到自身不生成预期的查询

[英]Python/SqlAlchemy joining table to itself not generating expected query

我想做的事情似乎应该很简单。 我想将代表数据收集站的表连接到自身,以便跟踪部署在同一位置的站的先前迭代。

在下面的代码中,我有两个类:StationTable 和 StationTypeTable。 StationTable 有两个 FK 关系——一个到站类型,另一个回到站表。

最后是生成的 SQL,它显示了对 StationType 表的正确连接,但没有任何痕迹由 previous_station 列创建的链接。

我究竟做错了什么? 请注意,这最终将与 FastApi 和 Async Postgres 驱动程序一起使用,这可能会或可能不会引起人们的兴趣。 另外,我不需要通过关系修改相关表; 我只需要读取一些属性。

使用 SQLAlchemy 1.4,最新版本。

from typing import Any

import sqlalchemy as sa
from sqlalchemy import select
from sqlalchemy.orm import registry, RelationshipProperty
from sqlalchemy.schema import ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm.decl_api import DeclarativeMeta


mapper_registry = registry()


class BaseTable(metaclass=DeclarativeMeta):
    __abstract__ = True
    registry = mapper_registry
    metadata = mapper_registry.metadata

    __init__ = mapper_registry.constructor
    id = sa.Column(UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()"))


class Relationship(RelationshipProperty):       # type: ignore
    """ Using this class hides some of the static typing messiness in SQLAlchemy. """
    inherit_cache = True        # If this works, should improve performance


    def __init__(self, *args: Any, **kwargs: Any):
        if "lazy" not in kwargs:
            # 'joined' means items should be loaded "eagerly" in the same query as that of the parent, using a JOIN or LEFT
            # OUTER JOIN. Whether the join is "outer" or not is determined by the relationship.innerjoin parameter.
            # We need this to keep loading in async mode from blowing up.
            # https://docs.sqlalchemy.org/en/14/orm/relationship_api.html
            kwargs["lazy"] = "joined"

        super().__init__(*args, **kwargs)


class StationTypeTable(BaseTable):
    __tablename__ = "station_type"

    name = sa.Column(sa.String(255), unique=True, nullable=False)
    description = sa.Column(sa.UnicodeText)


class StationTable(BaseTable):
    __tablename__ = "station"

    name = sa.Column(sa.String(255), unique=True, nullable=False)
    installation_date = sa.Column(sa.BigInteger, nullable=False)
    station_type_id = sa.Column(UUID(as_uuid=True), ForeignKey(StationTypeTable.id), nullable=False)
    previous_station = sa.Column(UUID(as_uuid=True), ForeignKey("station.id"), nullable=True)

    station_type_table = Relationship(StationTypeTable, uselist=False)
    previous_station_table = Relationship("StationTable", uselist=False)   # self join, uselist=False ==> one-to-one



query = select(StationTable)

print(query)


# SELECT station.id, station.name, station.installation_date, station.station_type_id, station.previous_station, 
#        station_type_1.id AS id_1, station_type_1.name AS name_1, station_type_1.description 
# FROM station 
# LEFT OUTER JOIN station_type AS station_type_1 ON station_type_1.id = station.station_type_id

编辑:

根据下面 Ian Wilson 的回复,我将参数join_depth=1添加到 previous_station_table 关系,这确实为关系生成了 SQL,但奇怪的是,与 station_type_table 关系相比,它是“错误的方式”。 这是使用该参数生成的 SQL:

SELECT station.id, station.name, station.installation_date, 
          station.station_type_id, station.previous_station, 
          station_type_1.id AS id_1, station_type_1.name AS name_1, 
          station_type_1.description, station_type_2.id AS id_2, 
          station_type_2.name AS name_2, station_type_2.description AS description_1, 
          station_1.id AS id_3, station_1.name AS name_3, 
          station_1.installation_date AS installation_date_1, 
          station_1.station_type_id AS station_type_id_1, 
          station_1.previous_station AS previous_station_1 
FROM station 
LEFT OUTER JOIN station_type AS station_type_1 
    ON station_type_1.id = station.station_type_id  -- looks right
LEFT OUTER JOIN station AS station_1 
    ON station.id = station_1.previous_station      -- looks backward, see below
LEFT OUTER JOIN station_type AS station_type_2 
    ON station_type_2.id = station_1.station_type_id

我认为标记的行应该是:

LEFT OUTER JOIN station AS station_1 ON station.previous_station = station_1.id

问题似乎是您必须为自引用预加载设置join_depth ,我将其设置为join_depth=1并且这似乎修复了查询。 由于您有一个额外的急切加入,这实际上会创建 3 个加入而不是 2 个加入,因为第二组站也必须加入类型。 该文档在此处解释了join_depth

配置自我参考急切加载

这里简要解释了relatoinship的参数:

sqlalchemy.orm.relationship.params.join_depth

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM