使用SQLAlchemy声明性ORM扩展时的多列索引
根据文档和sqlalchemy.Column
类中的注释,我们应该使用sqlalchemy.schema.Index
类来指定一个包含多个列的索引。
不过,例子中展示了如何直接使用表对象来做到这一点,如下所示:
meta = MetaData()
mytable = Table('mytable', meta,
# an indexed column, with index "ix_mytable_col1"
Column('col1', Integer, index=True),
# a uniquely indexed column with index "ix_mytable_col2"
Column('col2', Integer, index=True, unique=True),
Column('col3', Integer),
Column('col4', Integer),
Column('col5', Integer),
Column('col6', Integer),
)
# place an index on col3, col4
Index('idx_col34', mytable.c.col3, mytable.c.col4)
如果我们使用声明式ORM扩展,应该怎么做呢?
class A(Base):
__tablename__ = 'table_A'
id = Column(Integer, , primary_key=True)
a = Column(String(32))
b = Column(String(32))
我想在列"a"和"b"上建立一个索引。
2 个回答
25
为了补充@zzzeek的回答。
如果你想添加一个复合索引,并且使用ORM的声明式方法,可以这样做。
此外,我在研究SQLAlchemy的功能索引文档时,遇到了一些困难,想弄清楚如何替换mytable.c.somecol
。
from sqlalchemy import Index Index('someindex', mytable.c.somecol.desc())
我们可以直接使用模型的属性,并在其上调用.desc()
:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class GpsReport(db.Model):
__tablename__ = 'gps_report'
id = db.Column(db.Integer, db.Sequence('gps_report_id_seq'), nullable=False, autoincrement=True, server_default=db.text("nextval('gps_report_id_seq'::regclass)"))
timestamp = db.Column(db.DateTime, nullable=False, primary_key=True)
device_id = db.Column(db.Integer, db.ForeignKey('device.id'), primary_key=True, autoincrement=False)
device = db.relationship("Device", back_populates="gps_reports")
# Indexes
__table_args__ = (
db.Index('gps_report_timestamp_device_id_idx', timestamp.desc(), device_id),
)
如果你使用Alembic,我在用Flask-Migrate,它会生成类似这样的内容:
from alembic import op
import sqlalchemy as sa
# Added manually this import
from sqlalchemy.schema import Sequence, CreateSequence
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# Manually added the Sequence creation
op.execute(CreateSequence(Sequence('gps_report_id_seq')))
op.create_table('gps_report',
sa.Column('id', sa.Integer(), server_default=sa.text("nextval('gps_report_id_seq'::regclass)"), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=False))
sa.Column('device_id', sa.Integer(), autoincrement=False, nullable=False),
op.create_index('gps_report_timestamp_device_id_idx', 'gps_report', [sa.text('timestamp DESC'), 'device_id'], unique=False)
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index('gps_report_timestamp_device_id_idx', table_name='gps_report')
op.drop_table('gps_report')
# Manually added the Sequence removal
op.execute(sa.schema.DropSequence(sa.Sequence('gps_report_id_seq')))
# ### end Alembic commands ###
最后,你应该在你的PostgreSQL数据库中有以下表和索引:
psql> \d gps_report;
Table "public.gps_report"
Column | Type | Collation | Nullable | Default
-----------------+-----------------------------+-----------+----------+----------------------------------------
id | integer | | not null | nextval('gps_report_id_seq'::regclass)
timestamp | timestamp without time zone | | not null |
device_id | integer | | not null |
Indexes:
"gps_report_pkey" PRIMARY KEY, btree ("timestamp", device_id)
"gps_report_timestamp_device_id_idx" btree ("timestamp" DESC, device_id)
Foreign-key constraints:
"gps_report_device_id_fkey" FOREIGN KEY (device_id) REFERENCES device(id)
178
这些只是 Column
对象,设置 index=True 的标志是正常工作的:
class A(Base):
__tablename__ = 'table_A'
id = Column(Integer, primary_key=True)
a = Column(String(32), index=True)
b = Column(String(32), index=True)
如果你想要一个复合索引,Table
在这里依然存在,你只需要不声明它,其他一切都照常工作(确保你使用的是最近的 0.6 或 0.7 版本,这样在类声明完成后,A.a 的声明会被解释为 Column
):
class A(Base):
__tablename__ = 'table_A'
id = Column(Integer, primary_key=True)
a = Column(String(32))
b = Column(String(32))
Index('my_index', A.a, A.b)
在 0.7 版本中,Index
也可以放在 Table
的参数里,使用声明式时通过 __table_args__
来实现:
class A(Base):
__tablename__ = 'table_A'
id = Column(Integer, primary_key=True)
a = Column(String(32))
b = Column(String(32))
__table_args__ = (Index('my_index', "a", "b"), )