【问题标题】:SQLAlchemy, eager load postgresql INET/CIDR relationshipsSQLAlchemy,急切加载 postgresql INET/CIDR 关系
【发布时间】:2014-02-24 16:35:56
【问题描述】:

我在 postgresql 中有两张表,一张名为 ip_address,另一张名为 network

ip_address 表有两列:

  1. idINTEGER
  2. v4addressINET

network 表有两列:

  1. idINTEGER
  2. v4representationCIDR

我希望能够选择一个ip_address 并急切地加载ip_addresses network(s),而不必在表之间定义一个基于id 的外键关系。 id 列由与其他表的其他不相关关系使用。

完成此操作的 SQL 是:

select * from ip_address join network on ip_address.v4address << network.v4representation;

在 postgresql 中,&lt;&lt; 运算符可用于比较 INETCIDR。它将匹配INET 包含在CIDR 中的行。

我可以在我的 IPAddress 模型上定义一个 property 来完成此操作:

@property
def networks(self):
    query = session.query(Network)
    query = query.filter("v4representation >> :v4addr").params(v4addr=self.v4address)
    return query.all()

这可行,但是当我实际尝试在应用程序中使用此property 时,我遇到了典型的“N + 1”查询问题。我想以一种能够急切加载 ip 地址网络的方式来定义它。

我尝试使用primaryjoin 将其定义为relationship,但不知道需要什么。我试过这个:

networks = db.relationship("Network",
                           primaryjoin='IPAddress.v4address << Network.v4representation',
                           viewonly=True)

但是 sqlalchemy 不知道如何处理 &lt;&lt; 运算符,所以我切换到了这个:

networks = db.relationship("Network",
                           primaryjoin='IPAddress.v4address.op("<<")(Network.v4representation)',
                           viewonly=True)

但是 sqlalchemy 会抛出一个ArgumentError

ArgumentError: Could not locate any relevant foreign key columns for primary join condition 'public.ip_address.v4address << public.network.v4representation' on relationship IPAddress.networks.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation.

我尝试了几种为relationship 定义foreign_key 的组合:

networks = db.relationship("Network",
                           primaryjoin='IPAddress.v4address.op("<<")(Network.v4representation)',
                           foreign_keys='[Network.v4representation]',
                           viewonly=True)

但是 sqlalchemy 抛出 ArgumentErrors: ArgumentError:关系 IPAddress.networks 无法根据连接条件和 remote_side 参数确定任何明确的本地/远程列对。考虑使用 remote() 注释来准确标记连接条件中位于关系远程端的那些元素。

IPAddress.v4addressNetwork.v4representation 指定为remote_side 都不会更改异常。

尝试使用foreign/remote 注释primaryjoin 条件也没有任何帮助。

回到我最初的意图,我希望能够执行一个查询,该查询将返回 IP 地址并急切加载他们的网络(可能还有来自网络其他关系的数据,因为这是我的完整架构的简化)。

有人有什么建议吗?

提前致谢。

【问题讨论】:

    标签: python postgresql sqlalchemy


    【解决方案1】:

    这里缺少的部分是自定义运算符不在关系框架内工作。为了帮助解决这种情况,我为 SQLAlchemy 0.9.2 添加了一个新功能,即“is_comparison”标志,这里有一个示例,位于Using custom operators in join conditions

    这是一个使用较少公共 API 实现相同结果的版本,它也适用于 0.8:

    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.declarative import declarative_base
    
    from sqlalchemy.dialects.postgresql import INET, CIDR
    
    Base = declarative_base()
    
    # workaround before 0.9.2
    from sqlalchemy.sql import operators
    is_contained_by = operators.custom_op("<<")
    operators._comparison.add(is_contained_by)
    
    class IPA(Base):
        __tablename__ = 'ip_address'
    
        id = Column(Integer, primary_key=True)
        v4address = Column(INET)
    
        network = relationship("Network",
                            primaryjoin=lambda: is_contained_by(
                                         IPA.v4address, 
                                         (foreign(Network.v4representation))
                                        ),
                            viewonly=True
                        )
    class Network(Base):
        __tablename__ = 'network'
    
        id = Column(Integer, primary_key=True)
        v4representation = Column(CIDR)
    
    print Session().query(IPA).join(IPA.network)
    

    在 0.9.2 及更高版本中,可以这样做:

    class IPA(Base):
        __tablename__ = 'ip_address'
    
        id = Column(Integer, primary_key=True)
        v4address = Column(INET)
    
        network = relationship("Network",
                            primaryjoin="IPA.v4address.op('<<', is_comparison=True)"
                                "(foreign(Network.v4representation))",
                            viewonly=True
                        )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-25
      • 1970-01-01
      • 2014-10-13
      • 2018-05-15
      • 2021-04-28
      • 1970-01-01
      • 2015-03-12
      • 2019-02-19
      相关资源
      最近更新 更多