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

基于元类实现ORM:如何保留Car.brand的StringField实例?

问题描述

现有一段实现ORM表类的代码,定义了Car类作为数据库表的映射,但运行后Car.brand的值变成了None——这是因为元类TableMetaCls把所有数据库字段都设置为None了。请问如何让Car.brand保留StringField()实例?正确的实现方式是什么?该场景类似Django ORM的实现逻辑。

原代码如下:

class Field:
    pass


class StringField(Field):
    pass


class TableMetaCls(type):
    def __new__(cls, name, bases, attr):
        # backup field definition
        field_definitions = {}
        for x, y in attr.items():
            if isinstance(y, Field):
                field_definitions[x] = y
        attr['field_definitions'] = field_definitions

        # set field val to null
        for x, y in attr.items():
            if isinstance(y, Field):
                attr[x] = None

        return super(TableMetaCls, cls).__new__(cls, name, bases, attr)


class Table(metaclass=TableMetaCls):
    def save(self):
        pass


class Car(Table):
    brand = StringField()
    model = StringField()


c = Car()
c.brand = "BMW"

print(Car.brand)
正确实现方式

问题核心在于元类里的attr[x] = None操作直接覆盖了类属性的字段实例。正确思路是类属性保留字段元数据(StringField实例),用实例属性存储具体业务值,两者做明确区分。

修改后的完整代码:

class Field:
    pass


class StringField(Field):
    pass


class TableMetaCls(type):
    def __new__(cls, name, bases, attr):
        field_definitions = {}
        # 仅收集字段定义,不修改原类属性
        for x, y in attr.items():
            if isinstance(y, Field):
                field_definitions[x] = y
        attr['field_definitions'] = field_definitions
        # 移除覆盖字段实例的赋值操作
        return super().__new__(cls, name, bases, attr)


class Table(metaclass=TableMetaCls):
    def __init__(self, **kwargs):
        # 初始化实例属性,存储具体字段值
        for field_name in self.field_definitions:
            setattr(self, field_name, kwargs.get(field_name, None))
    
    def save(self):
        # 示例保存逻辑:从实例属性取业务值,结合字段元数据处理
        save_data = {
            field: getattr(self, field) 
            for field in self.field_definitions
        }
        print(f"保存数据: {save_data}")


class Car(Table):
    brand = StringField()
    model = StringField()


# 测试验证
c = Car(brand="BMW")
print(Car.brand)  # 输出: <__main__.StringField object at 0x...>(保留字段实例)
print(c.brand)    # 输出: BMW(实例存储业务值)
c.save()          # 输出: 保存数据: {'brand': 'BMW', 'model': None}

关键改动说明

  1. 去掉元类中attr[x] = None的代码,让Car.brand这类类属性始终保留StringField实例,用于存储字段的元数据(后续可扩展字段类型、约束等信息)。
  2. 在Table基类的__init__方法中初始化实例属性,用实例属性存储具体业务值,和类属性的字段元数据做区分,这和Django ORM的核心逻辑一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 08:45:37