Sqlalchemy跨多个列惟一

假设我有一个表示位置的类。地点“属于”顾客。位置由unicode的10个字符代码标识。“位置代码”在特定客户的位置中应该是唯一的。

The two below fields in combination should be unique
customer_id = Column(Integer,ForeignKey('customers.customer_id')
location_code = Column(Unicode(10))

如果我有两个客户,客户123和客户456。它们都可以有一个名为“main”的位置,但都不能有两个名为“main”的位置。

我可以在业务逻辑中处理这个问题,但我想确保没有办法轻松地在sqlalchemy中添加需求。unique=True选项似乎只在应用于特定字段时起作用,它会导致整个表对所有位置只有唯一的代码。

134677 次浏览

Column文档提取:

独特的 -当为True时,表示该列包含唯一的 约束,或者如果指数也是True,则表明索引 应该使用唯一标志创建。中指定多个列 约束/索引或要指定显式名称,请使用 UniqueConstraint指数显式构造

因为它们属于表而不是映射类,所以在表定义中声明它们,或者使用__table_args__中的声明:

# version1: table definition
mytable = Table('mytable', meta,
# ...
Column('customer_id', Integer, ForeignKey('customers.customer_id')),
Column('location_code', Unicode(10)),


UniqueConstraint('customer_id', 'location_code', name='uix_1')
)
# or the index, which will ensure uniqueness as well
Index('myindex', mytable.c.customer_id, mytable.c.location_code, unique=True)




# version2: declarative
class Location(Base):
__tablename__ = 'locations'
id = Column(Integer, primary_key = True)
customer_id = Column(Integer, ForeignKey('customers.customer_id'), nullable=False)
location_code = Column(Unicode(10), nullable=False)
__table_args__ = (UniqueConstraint('customer_id', 'location_code', name='_customer_location_uc'),
)
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()


class Location(Base):
__table_args__ = (
# this can be db.PrimaryKeyConstraint if you want it to be a primary key
db.UniqueConstraint('customer_id', 'location_code'),
)
customer_id = Column(Integer,ForeignKey('customers.customer_id')
location_code = Column(Unicode(10))

这个Python3的答案完全是衍生的,它只是把上面的所有东西都放在一个小的自包含的MySQL工作示例中。我需要在表上实现多对多关系的唯一性约束。也许你可以运行这个来调试本地环境问题,在我的情况下,它们纯粹是在键盘和椅子之间:)

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, ForeignKey, Integer, String, UniqueConstraint
from sqlalchemy.orm import relationship
import logging


logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector://user:pass@localhost/mydb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


user_role = db.Table(
'user_role',
Column('uid', String(6), ForeignKey('user.uid')),
Column('role', String(32), ForeignKey('role.name')),
UniqueConstraint('uid', 'role', name='idx_uid_role'))


class UserModel(db.Model):
__tablename__ = 'user'
uid = Column(String(6), primary_key=True)
create_time = Column(Integer, nullable=False)
login_time = Column(Integer, nullable=True)
roles = relationship('RoleModel', secondary='user_role',
backref='user', lazy='joined')


class RoleModel(db.Model):
__tablename__ = 'role'
name = Column(String(32), primary_key=True)
description = Column(String(256), nullable=False)


db.create_all()

运行此命令后,检查为表定义的索引,如下所示:

mysql> show index from user_role;

你应该看到:

+-----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table     | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| user_role |          0 | idx_uid_role |            1 | uid         | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| user_role |          0 | idx_uid_role |            2 | role        | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| user_role |          1 | role         |            1 | role        | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
3 rows in set (0.00 sec)

创建测试数据:

mysql> insert into user values ('abc123', 1, 2);
mysql> insert into role values ('role1', 'Description');
mysql> insert into user_role (uid, role) values ('abc123', 'role1');

最后,通过运行最后一次插入第二次测试唯一性约束,你应该看到:

mysql> insert into user_role (uid, role) values ('abc123', 'role1');
ERROR 1062 (23000): Duplicate entry 'abc123-role1' for key 'user_role.idx_uid_role'