python - example - Creando un índice único parcial con sqlalchemy en Postgres
sqlalchemy select (3)
SQLAlchemy admite la creación de índices parciales en postgresql .
¿Es posible crear un índice único parcial a través de SQLAlchemy?
Imagina una tabla / modelo como tal:
class ScheduledPayment(Base):
invoice_id = Column(Integer)
is_canceled = Column(Boolean, default=False)
Me gustaría un índice único donde solo pueda haber un pago programado "activo" para una factura determinada.
Puedo crear esto manualmente en postgres:
CREATE UNIQUE INDEX only_one_active_invoice on scheduled_payment
(invoice_id, is_canceled) where not is_canceled;
Me pregunto cómo puedo agregar eso a mi modelo SQLAlchemy utilizando SQLAlchemy 0.9.
En caso de que alguien se detenga buscando establecer una restricción única parcial con una columna que opcionalmente puede ser NULL
, aquí se explica cómo:
__table_args__ = (
db.Index(
''uk_providers_name_category'',
''name'', ''category'',
unique=True,
postgresql_where=(user_id.is_(None))),
db.Index(
''uk_providers_name_category_user_id'',
''name'', ''category'', ''user_id'',
unique=True,
postgresql_where=(user_id.isnot(None))),
)
donde user_id
es una columna que puede ser NULL
y quiero que se aplique una restricción única en las tres columnas (name, category, user_id)
con NULL
solo como uno de los valores permitidos para user_id
.
Para agregar a la respuesta por sas, postgresql_where no parece poder aceptar múltiples booleanos. Por lo tanto, en la situación en la que tiene DOS columnas con capacidad de anulación (supongamos una columna de ''precio'' adicional) no es posible tener cuatro índices parciales para todas las combinaciones de NULL / ~ NULL.
Una solución es usar valores predeterminados que nunca serían ''válidos'' (por ejemplo, -1 para el precio o '''' para una columna de Texto. Estos se compararían correctamente, por lo que no se permitirá que más de una fila tenga estos valores predeterminados.
Obviamente, también deberá insertar este valor predeterminado en todas las filas de datos existentes (si corresponde).
class ScheduledPayment(Base):
id = Column(Integer, primary_key=True)
invoice_id = Column(Integer)
is_canceled = Column(Boolean, default=False)
__table_args__ = (
Index(''only_one_active_invoice'', invoice_id, is_canceled,
unique=True,
postgresql_where=(~is_canceled)),
)