Flask-SQLAlchemy创建含多可空列的多列唯一约束方案
实现方案
两种方式都可以实现空值兼容的唯一约束效果,不需要额外插件。
1. ORM声明式配置(推荐,适配迁移工具)
SQLAlchemy 本身支持 PostgreSQL 的函数索引定义,不需要写原生SQL,直接替换你之前的UniqueConstraint配置即可。注意UniqueConstraint不支持传入函数表达式,需要用带unique=True参数的db.Index来定义:
from sqlalchemy import func class Location(db.Model): __tablename__ = 'location' id = db.Column(db.Integer, primary_key=True) longitude = db.Column(db.Float, nullable=False) latitude = db.Column(db.Float, nullable=False) address_1 = db.Column(db.String(50)) address_2 = db.Column(db.String(8)) country_id = db.Column(db.Integer, db.ForeignKey('country.id', ondelete='SET NULL')) city_id = db.Column(db.Integer, db.ForeignKey('city.id', ondelete='SET NULL')) __table_args__ = ( db.Index( 'unique_loc', longitude, latitude, func.COALESCE(address_1, ''), func.COALESCE(address_2, ''), country_id, city_id, unique=True ), )
配置完成后,如果你用Flask-Migrate(Alembic)做数据库迁移,该索引会被自动识别,生成的DDL语句和你手写的原生SQL完全一致。
补充说明:你的模型里country_id、city_id也是可空字段(外键配置了ondelete='SET NULL'),如果业务上要求这两个字段为NULL时也要判定为重复,需要给这两个字段也加上COALESCE转换,整数字段可以用一个业务中永远不会出现的无效值(比如-1)代替空字符串,写法为func.COALESCE(country_id, -1)、func.COALESCE(city_id, -1)。
2. 执行原生SQL创建
如果是已上线的存量表补索引,或者不想在模型层做声明,可以直接执行你写的原生SQL完成索引创建。Flask-SQLAlchemy 中执行方式如下:
# 在应用上下文内执行 with app.app_context(): db.session.execute(db.text(""" CREATE UNIQUE INDEX IF NOT EXISTS unique_loc ON location (longitude, latitude, COALESCE(address_1, ''), COALESCE(address_2, ''), country_id, city_id); """)) db.session.commit()
语句中加了IF NOT EXISTS,重复执行不会报错。你也可以把这段SQL放到Alembic迁移文件的op.execute()方法中,随迁移流程上线。
索引创建完成后,插入两条可空字段为NULL、其余字段完全一致的地点数据时,数据库会直接抛出唯一冲突错误,达到预期的去重效果。
内容的提问来源于stack exchange,提问作者jeldzinski
相关产品推荐
相关产品推荐

