Index de plusieurs colonnes lors de l'utilisation de l'extension ORM déclarative de sqlalchemy

95

Selon la documentation et les commentaires de la sqlalchemy.Columnclasse, nous devrions utiliser la classe sqlalchemy.schema.Indexpour spécifier un index contenant plusieurs colonnes.

Cependant, l'exemple montre comment le faire en utilisant directement l'objet Table comme ceci:

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)

Comment faire si nous utilisons l'extension déclarative ORM?

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

Je voudrais un index sur la colonne "a" et "b".

yorjo
la source
1
La question est un peu floue de savoir si vous voulez plusieurs index ou un seul index sur plusieurs colonnes (et j'étais plus confus avant de le modifier - à l'origine, il demandait délicieusement "un index contenant plusieurs index multiples" ). Mais peu importe, je suppose, puisque la réponse de zzzeek concerne les deux cas.
Mark Amery

Réponses:

138

ce ne sont que des Columnobjets, index = True flag fonctionne normalement:

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

si vous souhaitez un index composite, encore une fois Tableest présent ici comme d'habitude, vous n'avez tout simplement pas à le déclarer, tout fonctionne de la même manière (assurez-vous que vous êtes sur le récent 0.6 ou 0.7 pour que le wrapper déclaratif Aa soit interprété comme un Columnune fois la déclaration de classe terminée):

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)

En 0.7, le Indexpeut également être dans les Tablearguments, ce qui avec déclaratif est via __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
la source
1
Merci, j'ai mis à jour la version 0.7 et l'utilisation de table_args fonctionne très bien
yorjo
6
Que se passe-t-il si vous avez un dictionnaire pour table_args comme je le fais actuellement? table_args = {'mysql_engine': 'InnoDB'}
Nick Holden
7
Donc je suppose que je peux faire table_args = (Index ('my_index', "a", "b"), {'mysql_engine': 'InnoDB'})
Nick Holden
1
@RyanChou docs.sqlalchemy.org/en/latest/orm/extensions/declarative/… "Les arguments de mot-clé peuvent être spécifiés avec le formulaire ci-dessus en spécifiant le dernier argument sous forme de dictionnaire"
zzzeek
13

Pour compléter la réponse de @ zzzeek .

Si vous souhaitez ajouter un index composite avec DESC et utiliser la méthode déclarative ORM, vous pouvez procéder comme suit.

De plus, je luttais avec la documentation des index fonctionnels de SQSAlchemy, essayant de trouver un moyen de remplacer mytable.c.somecol.

from sqlalchemy import Index

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

Nous pouvons simplement utiliser la propriété model et l'appeler .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),
    )

Si vous utilisez Alembic, j'utilise Flask-Migrate, cela génère quelque chose comme:

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

Enfin, vous devriez avoir la table et les index suivants dans votre base de données 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
la source