Indeksowanie wielu kolumn podczas korzystania z deklaratywnego rozszerzenia ORM narzędzia sqlalchemy

97

Zgodnie z dokumentacją i komentarzami w sqlalchemy.Columnklasie powinniśmy użyć klasy sqlalchemy.schema.Indexdo określenia indeksu zawierającego wiele kolumn.

Jednak przykład pokazuje, jak to zrobić, używając bezpośrednio obiektu Table w następujący sposób:

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)

Jak mamy to zrobić, jeśli używamy deklaratywnego rozszerzenia ORM?

class A(Base):
    __tablename__ = 'table_A'
    id = Column(Integer, , primary_key=True)
    a = Column(String(32))
    b = Column(String(32))

Chciałbym mieć indeks w kolumnach „a” i „b”.

yorjo
źródło
1
Pytanie jest nieco niejasne, czy chcesz mieć wiele indeksów, czy jeden indeks w wielu kolumnach (i zanim go zredagowałem, byłem bardziej zdezorientowany - początkowo z zachwytem prosiłem o „indeks zawierający wiele indeksów wielokrotnych” ). Ale to chyba nie ma znaczenia, skoro odpowiedź Zzzeka odnosi się do obu przypadków.
Mark Amery,

Odpowiedzi:

141

to tylko Columnobiekty, index = True flaga działa normalnie:

class A(Base):
    __tablename__ = 'table_A'
    id = Column(Integer, primary_key=True)
    a = Column(String(32), index=True)
    b = Column(String(32), index=True)

jeśli chcesz mieć indeks złożony, ponownie Tablejest obecny tutaj, jak zwykle, po prostu nie musisz go deklarować, wszystko działa tak samo (upewnij się, że jesteś na ostatnim 0,6 lub 0,7, aby deklaratywne opakowanie Aa było interpretowane jako Columnpo zakończeniu deklaracji klasy):

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)

W wersji 0.7 Indexmoże być również w Tableargumentach, co w przypadku deklaratywnego odbywa się poprzez __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"), )
zzzeek
źródło
1
Dzięki, zaktualizowałem do 0.7 i używanie table_args działa dobrze
yorjo
6
Co się stanie, jeśli masz słownik dla table_args, taki jak ja obecnie? table_args = {'mysql_engine': 'InnoDB'}
Nick Holden
7
Więc chyba mogę zrobić table_args = (Index ('my_index', "a", "b"), {'mysql_engine': 'InnoDB'})
Nick Holden
1
@RyanChou docs.sqlalchemy.org/en/latest/orm/extensions/declarative/… "Argumenty słów kluczowych można określić w powyższym formularzu, podając ostatni argument jako słownik"
zzzeek
13

Aby zakończyć @ zzzeek za odpowiedź .

Jeśli chcesz dodać indeks złożony z DESC i użyć deklaratywnej metody ORM, możesz wykonać następujące czynności.

Co więcej, zmagałem się z dokumentacją indeksów funkcjonalnych SQSAlchemy, próbując wymyślić, jak zastąpić mytable.c.somecol.

from sqlalchemy import Index

Index('someindex', mytable.c.somecol.desc())

Możemy po prostu użyć właściwości model i wywołać .desc()ją:

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

Jeśli używasz Alembic, ja używam Flask-Migrate, generuje coś takiego:

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

Na koniec powinieneś mieć następującą tabelę i indeksy w swojej bazie danych 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)
Mickael
źródło