首先是对定义了一个Field类,这个Field类有两个属性,一个name,一个column_type,同时还可以打印字符串
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)
定义两种具体的Field子类,一种是StringField,一种是IntergerField,两种类型的column_type是固定的
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')
定义元类,这个元类是创建类的标准,接受四个参数:cls(当前准备创建的类的对象,具体我也不知道是啥),类的名字,类的父类,类的属性
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
#如果类的名字是Model的话,那么将不会修改Model中的任何内容,直接返回原类
if name=='Model':
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
#如果类的属性是Field对象的话,将类的属性写入mappings字典,如下面的user中的类属性{id = IntegerField('id'),...},会将id 作为k,IntegerField('id')作为v写入字典mappings中,并将这个mappings绑定到类属性__mappings__,将类名字name绑定到类属性__table__中,同时会删掉类属性,也就是说此时创建的user类中只剩下从父类Model继承的一些方法和metaclass给予的__mappings__和__table__属性
mappings = dict()
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)
创建父类Model,这个Model是dict的子类,所以他也接受关键字参数。__setattr__实现可以后面认为添加dict,__getattar__将字典中的key转化为了属性。举个例子,比如说你首先创建了s=Modle(id=5)这个实例,后面你想添加Model[lastname]=ling进入字典中,那么就可以用Model.setattr(lastname,ling)方法传入。用__setattr__和__getattr__可以完美实现实例新字典的创建和属性调用。最后Model类规定了一个save方法,它会将__mappings__中的v.name添加到列表fields中,之前说过IntegerField(‘id’)作为__mappings__中的v,那么就会将InterField(‘id’).name写入fileds中,也就是将id写入fields中。同时会将__mappings__中的k作为实例属性去寻找它的值(利用__getattr__方法)并写入列表args中。
class Model(dict, metaclass=ModelMetaclass):
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))
规定User类,首先给出了四个类属性 id,name,email,password,随后按照metaclass创建类,过程中将id,name,email,password作为k,IntergerField(‘id’)作为v写入mappings中,并将这四个类属性删掉,同时创建两个类属性,mappings__和__table。同时它还拥有父类Modle中的__getattr__方法和__setattr__方法,save方法
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
创建一个User实例,同时传入一个字典,最后保存。
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()
输出结果
Found model: User
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found mapping: id ==> <IntegerField:uid>
Found mapping: name ==> <StringField:username>
SQL: insert into User (password,email,username,id) values (?,?,?,?)
ARGS: ['my-pwd', 'test@orm.org', 'Michael', 12345]
总结,metaclass做了一下两几件事:
1、将要创建的类的符合条件属性和其指向的属性对象写入一个mapping中,完了之后删除该属性。
2、创建两个新的类属性__mappings__和__table__,同时没有对Model进行任何修改
而Model则是定义了子类的三种方法__setattr__、__getattr__和save