You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Peewee的资产管理数据库:实现主资产表关联不同类型子表的方案咨询

嘿,我完全懂你的需求——想要把通用资产存在主表,专属字段分到Image/Video子表,还能灵活关联,普通外键确实没法满足,因为它只能绑定单一模型。好在Peewee有两种靠谱的方案能解决这个问题,咱们一个个说:

方案一:用Peewee内置的多态模型(强烈推荐)

这是最贴合你需求的方式,Peewee原生支持多态继承,主表存通用字段,子类自动扩展专属字段,还会帮你处理关联逻辑,不用自己写额外代码。

代码实现

from peewee import *
import datetime

DB = SqliteDatabase('assets.db')

class Asset(Model):
    # 通用字段
    name = CharField()
    datetime = DateTimeField()
    miniature = CharField()
    preview = CharField()
    # 多态标识字段,用来区分是图片还是视频
    type_ = CharField(null=False)

    class Meta:
        database = DB
        # 告诉Peewee这是多态基类,指定类型字段
        polymorphic_on = type_

class Image(Asset):
    # 图片专属字段
    resolution_x = IntegerField()
    resolution_y = IntegerField()
    channels = CharField()
    layers = IntegerField()

    class Meta:
        # 给图片类型指定唯一标识
        polymorphic_identity = 'image'

class Video(Asset):
    # 视频专属字段
    resolution_x = IntegerField()
    resolution_y = IntegerField()
    framerate = FloatField()
    bitrate = FloatField()
    duration = FloatField()

    class Meta:
        # 给视频类型指定唯一标识
        polymorphic_identity = 'video'

怎么用?

创建表、增删改查都非常顺畅,Peewee会自动处理主表和子表的关联:

# 先创建所有表
DB.create_tables([Asset, Image, Video])

# 添加一张图片
sunset_img = Image.create(
    name='日落.jpg',
    datetime=datetime.datetime.now(),
    miniature='缩略图_日落.jpg',
    preview='预览图_日落.jpg',
    resolution_x=1920,
    resolution_y=1080,
    channels='RGB',
    layers=1
)

# 添加一段视频
vacation_vid = Video.create(
    name='旅行vlog.mp4',
    datetime=datetime.datetime.now(),
    miniature='缩略图_vlog.jpg',
    preview='预览_vlog.mp4',
    resolution_x=3840,
    resolution_y=2160,
    framerate=60.0,
    bitrate=50000.0,
    duration=120.5
)

# 查询所有资产,自动返回对应的子类实例
for asset in Asset.select():
    if isinstance(asset, Image):
        print(f"【图片】{asset.name} | 分辨率: {asset.resolution_x}x{asset.resolution_y}")
    elif isinstance(asset, Video):
        print(f"【视频】{asset.name} | 时长: {asset.duration}秒")

# 直接查询所有图片,能拿到所有专属字段
for img in Image.select():
    print(f"图片: {img.name} | 通道数: {img.channels}")

这个方案的好处是:

  • 数据一致性有保障,Peewee会自动维护主表和子表的关联
  • 查询时自动返回对应类型的实例,不用手动处理关联
  • 代码简洁,符合面向对象的设计

方案二:手动实现通用外键(适合需要高度自定义的场景)

如果你不想用多态继承,也可以手动实现通用外键——在主表存两个字段:一个type_标识资产类型,一个foreign_id存子表的主键ID,然后通过自定义方法关联子表记录。

代码实现

from peewee import *
import datetime

DB = SqliteDatabase('assets.db')

class Asset(Model):
    name = CharField()
    datetime = DateTimeField()
    miniature = CharField()
    preview = CharField()
    type_ = CharField(null=False)  # 可选值: 'image' / 'video'
    foreign_id = IntegerField(null=False)  # 对应子表的主键ID

    class Meta:
        database = DB

    def get_details(self):
        """获取对应的专属字段记录"""
        if self.type_ == 'image':
            return Image.get_by_id(self.foreign_id)
        elif self.type_ == 'video':
            return Video.get_by_id(self.foreign_id)
        else:
            raise ValueError(f"未知资产类型: {self.type_}")

class Image(Model):
    resolution_x = IntegerField()
    resolution_y = IntegerField()
    channels = CharField()
    layers = IntegerField()

    class Meta:
        database = DB

class Video(Model):
    resolution_x = IntegerField()
    resolution_y = IntegerField()
    framerate = FloatField()
    bitrate = FloatField()
    duration = FloatField()

    class Meta:
        database = DB

怎么用?

需要手动维护type_foreign_id的一致性:

DB.create_tables([Asset, Image, Video])

# 先创建图片子记录
img_record = Image.create(
    resolution_x=1920,
    resolution_y=1080,
    channels='RGB',
    layers=1
)
# 再创建主资产记录
Asset.create(
    name='日落.jpg',
    datetime=datetime.datetime.now(),
    miniature='缩略图_日落.jpg',
    preview='预览图_日落.jpg',
    type_='image',
    foreign_id=img_record.id
)

# 查询资产并获取专属详情
asset = Asset.get(Asset.name == '日落.jpg')
img_details = asset.get_details()
print(f"分辨率: {img_details.resolution_x}x{img_details.resolution_y}")

这个方案的优点是灵活,但缺点也很明显:

  • 需要手动维护关联关系,容易出错(比如foreign_id指向不存在的子记录)
  • 查询时需要额外调用方法获取子表数据,不如多态模型方便

总结

如果没有特殊的自定义需求,多态模型绝对是最优解——Peewee已经帮你把所有关联逻辑都封装好了,代码简洁还能保证数据一致性。

内容的提问来源于stack exchange,提问作者Websterek

火山引擎 最新活动