Индекс нескольких столбцов при использовании декларативного расширения ORM sqlalchemy


97

Согласно документации и комментариям в sqlalchemy.Columnклассе, мы должны использовать класс sqlalchemy.schema.Indexдля указания индекса, который содержит несколько столбцов.

Однако в примере показано, как это сделать, напрямую используя объект Table следующим образом:

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))

Мне нужен указатель столбцов «а» и «б».


1
Вопрос немного неясен, хотите ли вы использовать несколько индексов или один индекс для нескольких столбцов (и был более запутанным, прежде чем редактировать его - первоначально он восхитительно просил «индекс, содержащий несколько нескольких индексов» ). Но это неважно, я думаю, поскольку ответ zzzeek касается обоих случаев.
Марк Эмери

Ответы:


142

это просто 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, чтобы декларативная оболочка Aa интерпретировалась как 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"), )

1
Спасибо, я обновился до 0.7, и использование table_args отлично работает
yorjo

6
Что произойдет, если у вас есть словарь для table_args, как у меня сейчас? table_args = {'mysql_engine': 'InnoDB'}
Ник Холден,


7
Думаю, я могу сделать table_args = (Index ('my_index', «a», «b»), {'mysql_engine': 'InnoDB'})
Ник Холден

1
@RyanChou docs.sqlalchemy.org/en/latest/orm/extensions/declarative/… «Аргументы ключевого слова могут быть указаны в форме выше, указав последний аргумент как словарь»
zzzeek

13

Чтобы завершить ответ @zzzeek .

Если вы хотите добавить составной индекс с помощью DESC и использовать декларативный метод ORM, вы можете сделать следующее.

Кроме того, я боролся с документацией по функциональным индексам SQSAlchemy, пытаясь выяснить, как заменить 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)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.