8.6 通过元素实现ORM¶
首先明确需求
# 简单定义
class User:
name = CharFiled(db_column="", max_length=32)
age = IntFiled(db_column="", min_value=0, max_value=100)
class Meta:
db_table = 'user'
# ORM
user = User()
user.name = 'linda'
user.age = 18
user.save()
迷你版 ORM
from collections import OrderedDict
class Field:
pass
class IntField(Field):
def __init__(self, db_column, min_value=0, max_value=100):
self.db_column = db_column
self.min_value = min_value
self.max_value = max_value
self._value = None
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('need int value')
if value < self.min_value or value > self.max_value:
raise ValueError('need [%s, %s] value' % (self.min_value, self.max_value))
self._value = value
class CharField(Field):
def __init__(self, db_column, max_length=32):
self.db_column = db_column
self.max_length = max_length
self._value = None
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError('need str value')
if len(value) > self.max_length:
raise ValueError('len need lower than %s' % self.max_length)
self._value = value
# 元类注入一系列属性
class MetaClass(type):
def __new__(cls, name, bases, attrs, **kw):
# BaseModel 也会调用 Metaclass,但没有定义 name,age 等属性,可特殊判断
if name == 'BaseModel':
return super().__new__(cls, name, bases, attrs, **kw)
fields = {}
for key, value in attrs.items():
if isinstance(value, Field):
fields[key] = value
attrs_meta = attrs.get('Meta', None)
_meta = {}
db_table = name.lower()
if attrs_meta is not None:
table = getattr(attrs_meta, 'db_table', None)
if not table:
db_table = table
_meta['db_table'] = db_table
attrs['_meta'] = _meta
attrs['fields'] = fields
if attrs.get('Meta'):
del attrs['Meta']
return super().__new__(cls, name, bases, attrs, **kw)
class BaseModel(metaclass=MetaClass):
def __init__(self, **kw):
for key, value in kw.items():
setattr(self, key, value)
super().__init__()
def save(self):
fields = OrderedDict(self.fields)
fields_str = ", ".join([value.db_column for value in fields.values()])
values_str = ', '.join([str(getattr(self, field)) if not isinstance(value, CharField)
else "'%s'" % str(getattr(self, field))
for field, value in fields.items()])
sql = "insert into %s (%s) values (%s)" % (self._meta['db_table'], fields_str, values_str)
print(sql)
# insert into user (name1, age) values ('linda', 20)
# 自定义类时写少量属性,元类帮助我们注入很多通用属性
class User(BaseModel):
name = CharField('name1', max_length=16)
age = IntField('age', min_value=0, max_value=100)
class Meta:
db_table = 'user'
if __name__ == '__main__':
user = User(name='linda')
user.age = 20
user.save()
ORM 设计思想
- 数据属性描述符(__set__, __get__) 实现验证操作
- 自定义元类(MetaClass(type)) 实现参数注入
- 自定义 ORM 类(BaseModel) 获取元类注入的参数 进行额外操作
- 自定义元类 注入 objects
- 需特别注意调用层级顺序,__new__ 在 __init__ 之前,所以 __init__ 中可以使用元类注册测参数