代码
# -*- coding: utf-8 -*-
' Simple ORM using metaclass '
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__, self.name)
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint')
class ModelMetaclass(type):
# 元类实现了对model类的定制, 通过User类的继承请求中的参数, 来实现对Model这个类的定制
# 下面的User(xxx), 经过元类的定制后, 用来创建User的类增加了一个__mapping__字典和___table__属性
def __new__(cls, name, bases, attrs):
if name=='Model':
# 这里是为了排除掉对Model类的修改, 试想一下当因为创建了一个User对象然后创建了一个带有属性的Model类,
# 然后又因为某些原因想要单独创建一个Model类的时候, 如果没有这一步会创造出一个带有部分属性的Model
# 总之就是排除了在某个子类调用Model时候造成的修改
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
mappings = dict()
# 在这里打印了之后会发现每个都是field
print(attrs)
for k, v in attrs.items():
if isinstance(v, Field):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs)
class Model(dict, metaclass=ModelMetaclass):
# Model是以dict为父类, 所以通过传入的参数确定dict的内容
def __init__(self, **kw):
super(Model, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
# testing code:
class User(Model):
# 这里的几个id, name, email和password都是类变量
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 这里面的键值对都是用来初始化User这个dict的
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
print(u.id)
u.save()
解读
创建一个新的User步骤
- 首先在创建一个User对象的时候, 先去找了User的父类Model
- 找到了Model并且将要加载这个类的时候, 发现Model是定义了元类的, 所以接下来要去找元类
- 在元类的__new__方法中, 由于这个调用是从User发起的, 所以带进来的参数都是User里面的参数
- 在元类中如果打印了attrs会发现都是field的子类, 可能有人会有疑惑为什么不是User括号中的字符串或者是整数
- 原因是我们目前还处在定制Model类的过程中, 而User括号里面的是用来初始化对象的参数
- 所以说用来初始化类的参数当然就是User类中的参数了
- 判断是定制Model类还是要初始化一个Model类
- 根据传进来的参数(User的类变量), 给定制的Model类添加一些属性
- 创建定制好的Model类
- 创建User对象, 并且用传进去的参数初始化这个魔改版dict