在处理继承/混合时,您应该使用__table_args__ 将您希望为所有类及其底层Table 创建的索引作为“内联”定义传递,在这种情况下它也应该是一个声明的属性,如在"Creating Indexes with Mixins"中解释:
class Event(Base):
@declared_attr
def user_id(cls):
return Column(BigInteger, ForeignKey("users.user_id"), nullable=False)
@declared_attr
def __table_args__(cls):
# Return a tuple of arguments to pass to Table
return (Index(f'idx_{cls.__tablename__}_user_id', 'user_id'),)
这将避免为不同(子)类创建的索引之间的名称冲突。请注意,这里的“内联”表单使用字符串名称来标识要索引的列,但cls.foo_id 在声明的属性中也可以正常工作。一般来说,不需要将Index 指定为模型属性,在某些情况下也不需要指定it may even lead to confusion。
索引列的简单解决方案是将index=True 传递给Column。这是为相关列创建匿名索引的快捷方式:
class Event(Base):
@declared_attr
def user_id(cls):
return Column(BigInteger, ForeignKey("users.user_id"), nullable=False, index=True)
当您不处理继承/混合时,您不需要 @declared_attr 包装:
class MyModel(Base):
foo = Column(Integer)
bar = Column(Integer)
# "Inline", but gets the actual column instead of a string name.
# Possible with Declarative.
__table_args__ = (Index('idx_mymodel_foo', foo),)
# Finds the table through the Column used in the definition.
Index('idx_mymodel_bar', MyModel.bar)
您收到错误的原因是,在类构建期间,会评估类定义的主体,然后将生成的命名空间用作类的命名空间。在评估期间
idx_event_user_id = Index('idx_event_user_id', user_id)
导致Index 按原样接收分配给该命名空间中user_id 的声明属性descriptor 对象,因此SQLAlchemy 抱怨。
当您通过构造的类对象或它的实例访问描述符时,它们会做自己的事情,在declared attribute 的情况下,这意味着它会评估映射属性或它所代表的特殊声明性成员。