Python-元类实现ORM

元类实现ORM

ORM是什么
ORM 是 python编程语言后端web框架 Django的核心思想,“Object Relational Mapping”,
即对象-关系映射,简称ORM。

一个句话理解就是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,
当对这个实例对象操作时,能够对应MySQL语句
说明
所谓的ORM就是让开发者在操作数据库的时候,能够像操作对象时通过xxxx.属性=yyyy一样简单,
这是开发ORM的初衷

只不过ORM的实现较为复杂,Django中已经实现了很复杂的操作,我们主要理解其中的意义

通过元类简单实现ORM中的insert功能

定义元类
class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # 删除这些已经在字典中存储的属性
        for k in mappings.keys():
            attrs.pop(k)

        # 将之前的uid/name/email/password以及对应的对象引用、类名字
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class User(metaclass=ModelMetaclass):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    # 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储

    # 以上User类中有
    # __mappings__ = {
    #     "uid": ('uid', "int unsigned")
    #     "name": ('username', "varchar(30)")
    #     "email": ('email', "varchar(30)")
    #     "password": ('password', "varchar(30)")
    # }
    # __table__ = "User"

    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0])
            args.append(getattr(self, k, None))

        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
        print('SQL: %s' % sql)


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
print("-"*70)
print(u.__dict__)
u.save()

打印结果:
Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
----------------------------------------------------------------------
{'uid': 12345, 'name': 'Michael', 'email': 'test@orm.org', 'password': 'my-pwd'}
SQL: insert into User (uid,username,email,password) values (12345,Michael,test@orm.org,my-pwd)

完善对数据类型的检测

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # 删除这些已经在字典中存储的属性
        for k in mappings.keys():
            attrs.pop(k)

        # 将之前的uid/name/email/password以及对应的对象引用、类名字
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class User(metaclass=ModelMetaclass):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    # 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储

    # 以上User类中有
    # __mappings__ = {
    #     "uid": ('uid', "int unsigned")
    #     "name": ('username', "varchar(30)")
    #     "email": ('email', "varchar(30)")
    #     "password": ('password', "varchar(30)")
    # }
    # __table__ = "User"

    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0])
            args.append(getattr(self, k, None))

        args_temp = list()
        for temp in args:
            # 判断入如果是数字类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        # print(args_temp) # ['12345', "'Michael'", "'test@orm.org'", "'my-pwd'"]
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        print('SQL: %s' % sql)


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
print("-"*70)
print(u.__dict__)
u.save()

打印结果:
Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
----------------------------------------------------------------------
{'uid': 12345, 'name': 'Michael', 'email': 'test@orm.org', 'password': 'my-pwd'}
['12345', "'Michael'", "'test@orm.org'", "'my-pwd'"]
SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 学习一下几个内容 __getattr__和__setattr__方法,把未定义的属性获取和所有的属性赋值指向通用的...
    低吟浅唱1990阅读 3,097评论 0 0
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 9,703评论 4 16
  • 70.魔法方法:http://bbs.fishc.com/forum.php?mod=viewthread&tid...
    杨大菲阅读 3,945评论 0 0
  • 2018年2月2日 中雪 亲子日记第四天 今天是返校的日子,照常还是早上六点起床,宇鹏依旧准时去书房读英语半...
    高宇鹏阅读 1,702评论 0 1
  • 感恩节,一个西方传统的节日,但感恩对每个人来说都是时刻不在的,它提醒我们在前进的路上有这么多相伴,相知,相爱的人,...
    柳桃小姐阅读 2,695评论 0 1

友情链接更多精彩内容