【问题标题】:How to handle multiple join paths properly with automap?如何使用自动映射正确处理多个连接路径?
【发布时间】:2021-03-29 12:28:21
【问题描述】:

我尝试将现有数据库自动映射到类。在两个表之间,我有多个连接路径,我无法使用 SQLAlchemy 正确管理这些路径。

这是我数据库中的示例架构:

CREATE SCHEMA shop;

CREATE TABLE shop.address (
    id SERIAL PRIMARY KEY,
    name text,
    address text
)

CREATE TABLE shop.orders (
    id SERIAL PRIMARY KEY,
    items text,
    billingaddr_id integer REFERENCES address,
    shippingaddr_id integer REFERENCES address
);

我已经声明了这些外键的关系如下:

from sqlalchemy import create_engine
from sqlalchemy.orm import relationship, Session
from sqlalchemy.ext.automap import automap_base

engine = create_engine(
    "postgresql://postgres:postgres@localhost:5432/postgres",
    future=True
)
Base = automap_base()


class Order(Base):
    __tablename__ = 'orders'
    __table_args__ = {"schema": "shop"}
    billingaddr = relationship('address', foreign_keys="Order.billingaddr_id", backref="orders_billed")
    shippingaddr = relationship('address', foreign_keys="Order.shippingaddr_id", backref="orders_shipped")


Base.prepare(engine, schema='shop', reflect=True)

Address = Base.classes.address

现在当创建一个新的地址对象(jack)时:

jack = Address(name='Jack', address='57815 Cheryl Unions')

我收到警告:

"SAWarning: 关系 'Order.address' 会将列 address.id 复制到列 orders.shippingaddr_id,这与关系冲突:'address.orders_shipped' (将 address.id 复制到 orders.shippingaddr_id), 'Order .shippingaddr'(将 address.id 复制到 orders.shippingaddr_id)。如果这不是本意,请考虑这些关系是否应该与 back_populates 链接,或者如果 viewonly=True 应该应用于一个或多个,如果它们是只读的。对于外键约束部分重叠的不太常见的情况,orm.foreign() 注释可用于隔离应写入的列。'overlaps' 参数可用于删除此警告。"

应该如何解决?

address 关系是自动创建的一个自动映射,现在让我很困惑。我实际上不再需要那个,因为我自己建立了关系。我可以以某种方式阻止自动映射默认创建它,还是可以删除创建的不必要的关系?我试图在类声明中设置address = None,但它没有用。 address 关系仍在创建中。

【问题讨论】:

    标签: python postgresql sqlalchemy


    【解决方案1】:

    在我解决这个问题后回答我自己的问题(感谢 sqlalchemy mailing list 的 Mike)。

    默认情况下,sqlalchemy 1.14 对两个关系使用相同的名称,因此后者会覆盖先前创建的。可以覆盖这些关系的命名,将这些命名方法的自定义实现传递给automap.base.prepare。我实现了那些为每个关系提供唯一名称。这样我什至不需要使用显式的声明类样式。

    from sqlalchemy import create_engine
    from sqlalchemy.orm import relationship, sessionmaker, backref
    from sqlalchemy.ext.automap import automap_base, generate_relationship
    
    engine = create_engine(
        "postgresql://postgres:postgres@localhost:5432/postgres",
        future=True
    )
    Base = automap_base()
    
    
    def name_for_scalars(base, local_cls, referred_cls, constraint):
        if local_cls.__name__ == 'orders' and referred_cls.__name__ == 'address':
            if constraint.name == 'orders_billingaddr_id_fkey':
                return 'billingaddr'
            elif constraint.name == 'orders_shippingaddr_id_fkey':
                return 'shippingaddr'
        return referred_cls.__name__.lower()
    
    
    def name_for_collections(base, local_cls, referred_cls, constraint):
        if local_cls.__name__ == 'address' and referred_cls.__name__ == 'orders':
            if constraint.name == 'orders_billingaddr_id_fkey':
                return 'orders_billed'
            elif constraint.name == 'orders_shippingaddr_id_fkey':
                return 'orders_shipped'
        return referred_cls.__name__.lower() + "_collection"
    
    Base.prepare(
        autoload_with=engine,
        schema='shop',
        name_for_scalar_relationship=name_for_scalars,
        name_for_collection_relationship=name_for_collections
    )
    
    Order = Base.classes.orders
    Address = Base.classes.address
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多