廖雪峰Python教程中简单ORM代码解读

代码

# -*- 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步骤

  1. 首先在创建一个User对象的时候, 先去找了User的父类Model
  2. 找到了Model并且将要加载这个类的时候, 发现Model是定义了元类的, 所以接下来要去找元类
  3. 在元类的__new__方法中, 由于这个调用是从User发起的, 所以带进来的参数都是User里面的参数
    • 在元类中如果打印了attrs会发现都是field的子类, 可能有人会有疑惑为什么不是User括号中的字符串或者是整数
    • 原因是我们目前还处在定制Model类的过程中, 而User括号里面的是用来初始化对象的参数
    • 所以说用来初始化类的参数当然就是User类中的参数了
  4. 判断是定制Model类还是要初始化一个Model类
  5. 根据传进来的参数(User的类变量), 给定制的Model类添加一些属性
  6. 创建定制好的Model类
  7. 创建User对象, 并且用传进去的参数初始化这个魔改版dict
上一篇:一起Talk Android吧(第一百三十二回:Android自定义View九)


下一篇:~~番外:说说Python 面向对象编程~~