如何在Peewee中实现MariaDB的ENUM与SET数据类型功能
方案1:自定义Peewee字段适配MariaDB原生SET类型
该方案完全贴合你原有SQL字段定义,无需修改现有表结构、无需做数据迁移,和你之前用CHOICES实现ENUM的逻辑高度一致。
首先实现自定义MariaDBSetField类:
from peewee import Field, SQL class MariaDBSetField(Field): field_type = 'SET' def __init__(self, choices, *args, **kwargs): self.choices = choices # 拼接SET类型的可选值DDL self.ddl = f"SET({','.join(repr(c) for c in choices)})" super().__init__(*args, **kwargs) def ddl_datatype(self, ctx): return self.ddl def db_value(self, value): # 将Python侧传入的列表/元组转成数据库需要的逗号分隔字符串 if isinstance(value, (list, tuple)): # 可选值校验 invalid_vals = [v for v in value if v not in self.choices] if invalid_vals: raise ValueError(f"非法选项: {invalid_vals}, 可选值为{self.choices}") return ','.join(value) return value def python_value(self, value): # 将数据库读出的逗号分隔字符串转成Python列表 if not value: return [] return value.split(',')
在模型中直接使用即可:
# 定义端口可选列表 PORT_CHOICES = ('443','587','636','990','993','995','1054','3128','3129','3306','5432','6697','8000','8443','9443','61617') # 原有状态选项 STATUS_CHOICES = ( (0,'New'), (1,'Processing'), (2,'Installed')) class Catalog(BaseModel): status = CharField(max_length=10, verbose_name='Status', choices=STATUS_CHOICES, default='New', constraints=[SQL("DEFAULT 'New'")]) ports = MariaDBSetField( choices=PORT_CHOICES, verbose_name='端口', null=False, default=['443'], constraints=[SQL("DEFAULT '443'")] )
使用示例:
# 写入多选端口数据 Catalog.create(ports=['443', '9443', '3128']) # 读取数据,自动转成列表 item = Catalog.get_by_id(1) print(item.ports) # 输出 ['443', '9443', '3128'] # 筛选包含443端口的记录 Catalog.select().where(SQL("FIND_IN_SET('443', ports) > 0"))
方案2:使用BitField实现跨数据库兼容的多选存储
如果后续有换数据库的需求,可以用位字段实现,完全不依赖数据库的SET类型,存储效率更高。本质是每个端口对应一个二进制位,用整数存储多选结果。
from peewee import BitField # 每个端口对应一个唯一的位标记 PORT_OPTIONS = { '443': 1 << 0, '587': 1 << 1, '636': 1 << 2, '990': 1 << 3, '993': 1 << 4, '995': 1 << 5, '1054': 1 << 6, '3128': 1 << 7, '3129': 1 << 8, '3306': 1 << 9, '5432': 1 << 10, '6697': 1 << 11, '8000': 1 << 12, '8443': 1 << 13, '9443': 1 << 14, '61617': 1 << 15, } class Catalog(BaseModel): ports = BitField(default=PORT_OPTIONS['443']) # 批量生成端口筛选快捷属性 for port, flag in PORT_OPTIONS.items(): setattr(Catalog, f'port_{port}', Catalog.ports.flag(flag))
使用示例:
# 写入多选端口 Catalog.create(ports=PORT_OPTIONS['443'] | PORT_OPTIONS['9443']) # 筛选包含443端口的记录 Catalog.select().where(Catalog.port_443) # 读取数据转成端口列表 item = Catalog.get_by_id(1) selected_ports = [port for port, flag in PORT_OPTIONS.items() if item.ports & flag]
两种方案都不需要额外建关联表,第一种完全兼容你现有的MariaDB表结构和历史数据,第二种跨数据库兼容性更好,可根据实际需求选择。
内容的提问来源于stack exchange,提问作者Meli
相关产品推荐
相关产品推荐

