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


95

Відповідно до документації та коментарів до 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 стосується обох випадків.
Mark Amery

Відповіді:


138

це просто 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 = {'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())

Ми можемо просто скористатися властивістю model і викликати .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.