You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 10:15:04