Saltar al contenido

Partición postgresql y sqlalchemy

Si hallas alguna incompatibilidad en tu código o proyecto, recuerda probar siempre en un entorno de testing antes añadir el código al proyecto final.

Solución:

Puedes usar un MeasureMixin que ambas clases pueden heredar. Y luego usa un event para adjuntar la partición de la tabla.

from sqlalchemy import event

class MeasureMixin:
    city_id = Column(Integer, not_null=True)
    log_date = Column(Date, not_null=True)
    peaktemp = Column(Integer)
    unitsales = Column(Integer)

class Measure(MeasureMixin, Base):
    __tablename__ = 'measures'
    __table_args__ = 
        postgresql_partition_by: 'RANGE (log_date)'
    

class Measure2020(MeasureMixin, Base):
    __tablename__ = 'measures2020'

Measure2020.__table__.add_is_dependent_on(Measure.__table__)

event.listen(
    Measure2020.__table__,
    "after_create",
    DDL("""ALTER TABLE measures ATTACH PARTITION measures2020
VALUES FROM ('2020-01-01') TO ('2021-01-01');""")
)

Tuve un problema similar. Encontré la respuesta de @ moshevi bastante útil y terminé generalizándola un poco (ya que tenía muchas tablas para particionar).

Primero, crea una metaclase como esta:

from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.sql.ddl import DDL
from sqlalchemy import event


class PartitionByYearMeta(DeclarativeMeta):
    def __new__(cls, clsname, bases, attrs, *, partition_by):
        @classmethod
        def get_partition_name(cls_, key):
            # 'measures' -> 'measures_2020' (customise as needed)
            return f'cls_.__tablename___key'
        
        @classmethod
        def create_partition(cls_, key):
            if key not in cls_.partitions:
                
                Partition = type(
                    f'clsnamekey', # Class name, only used internally
                    bases,
                    '__tablename__': cls_.get_partition_name(key)
                )
                
                Partition.__table__.add_is_dependent_on(cls_.__table__)
                
                event.listen(
                    Partition.__table__,
                    'after_create',
                    DDL(
                        # For non-year ranges, modify the FROM and TO below
                        f"""
                        ALTER TABLE cls_.__tablename__
                        ATTACH PARTITION Partition.__tablename__
                        FOR VALUES FROM ('key-01-01') TO ('key+1-01-01');
                        """
                    )
                )
                
                cls_.partitions[key] = Partition
            
            return cls_.partitions[key]
        
        attrs.update(
            
                # For non-RANGE partitions, modify the `postgresql_partition_by` key below
                '__table_args__': attrs.get('__table_args__', ())
                + (dict(postgresql_partition_by=f'RANGE(partition_by)'),),
                'partitions': ,
                'partitioned_by': partition_by,
                'get_partition_name': get_partition_name,
                'create_partition': create_partition
            
        )
        
        return super().__new__(cls, clsname, bases, attrs)

A continuación, para cualquier tabla de su modelo que desee particionar:

class MeasureMixin:
    # The columns need to be pulled out into this mixin
    # Note: any foreign key columns will need to be wrapped like this:

    @declared_attr
    def city_id(self):
        return Column(ForeignKey('cities.id'), not_null=True)
    
    log_date = Column(Date, not_null=True)
    peaktemp = Column(Integer)
    unitsales = Column(Integer)

class Measure(MeasureMixin, Base, metaclass=PartitionByYearMeta, partition_by='logdate'):
    __tablename__ = 'measures'

Esto facilita agregar más tablas y particionar por cualquier número de valores.

Crear una nueva partición sobre la marcha funciona así:

# Make sure you commit any session that is currently open, even for select queries:
session.commit()

Partition = Measure.create_partition(2020)
if not engine.dialect.has_table(Partition.__table__.name):
    Partition.__table__.create(bind=engine)

Ahora la partición para key 2020 se crea y se pueden insertar valores para ese año.

Si te gusta el tema, eres capaz de dejar un enunciado acerca de qué te ha parecido este artículo.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *