关于廖雪峰教程元类在ORM的应用

首先是对定义了一个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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值