基于元类实现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}
关键改动说明
- 去掉元类中
attr[x] = None的代码,让Car.brand这类类属性始终保留StringField实例,用于存储字段的元数据(后续可扩展字段类型、约束等信息)。 - 在
Table基类的__init__方法中初始化实例属性,用实例属性存储具体业务值,和类属性的字段元数据做区分,这和Django ORM的核心逻辑一致。
内容的提问来源于stack exchange,提问作者user1187968
相关产品推荐
相关产品推荐

