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.