web-dev-qa-db-fra.com

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

Selon la documentation et les commentaires dans le sqlalchemy.Column classe, nous devrions utiliser la classe sqlalchemy.schema.Index pour 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 devrions-nous le faire si nous utilisons l'extension déclarative ORM?

class A(Base):
    __table= '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".

78
yorjo

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

class A(Base):
    __table= '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 Table est 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 la version 0.6 ou 0.7 récente pour la déclaration Aa wrapper à interpréter comme Column une fois la déclaration de classe terminée):

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

Index('my_index', A.a, A.b)

Dans 0.7, le Index peut aussi être dans les arguments Table, qui avec déclaratif se fait via __table_args__:

class A(Base):
    __table= 'table_A'
    id = Column(Integer, primary_key=True)
    a = Column(String(32))
    b = Column(String(32))
    __table_args__ = (Index('my_index', "a", "b"), )
116
zzzeek

Pour compléter @ zzzeek réponse .

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 me débattais avec la documentation Functional Indexes 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 appeler .desc() dessus:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class GpsReport(db.Model):
    __table= '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 le tableau 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)
6
Mickael