orm

orm

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

在对orm进行架构时首先要分清数据库和对象之间的映射关系:

对象与类 数据库
类名 表名
对象 一条记录
对象.属性 字段

这里将数据库的增删改查全部封装为一个个的方式,比如:save,delete,update,select等

具体思路:

一、数据类型类的定义

对字段可能用到的数据类型创建类,然后将这些类实例化出的对象作为字段类的属性。

抽象出父类

class Field:
    def __init__(self,name,column_type,primary_key,default):
        self.name = name
        self.column_type = column_type
        self.primary_key = primary_key
        self.default = default

子类

class IntegerField(Field):
    def __init__(self,name,column_type='int',primary_key=False,default=0):
        super().__init__(name,column_type,primary_key,default)

class StringField(Field):
        def __init__(self,name,column_type='varchar(64)',
                     primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)

二、定义表类

抽象出表的父类,这里表类继承字典是为了解决每一张表可以随意添加字段的问题,我们不能每实例化一张表就在表类的__init__中定义新添加的字段名,这样太麻烦了。通过继承字典,内部的__init__,可以接收任意多个关键字参数。

class Models(dict,metaclass=OrmMetaclass):
    def __getattr__(self, item):
        #调用没有的属性时触发
        return self.get(item)

    def __setattr__(self, key, value):
        #实现将对象.属性=属性值转化为字典的赋值操作
        #给字典对象本身赋值
        self[key] = value

子类

class User(Models):
    #属性名最好与字段类型的名字同名
    user_id = IntegerField(name='user_id',primary_key=True)
    user_name = StringField(name='name')
    pwd = StringField(name='pwd')
    #这些属性都是字段类型类实例化出来的

class Movie(Models):
    user_id = IntegerField(name='user_id',primary_key=True)
    user_name = StringField(name='name')
    pwd = StringField(name='pwd')

三、引入元类

继承字典的类实例化的对象,无法通过‘对象.属性’的方式存取值,我们通过__setattr__,__getattr__来实现,让字典对象与普通对象一模一样,并且具备字典对象原有的特性。

元类需要处理的问题:

  1. 强制数据表类有且只有一个主键。
  2. 将数据表中所有的字段对象都存放在一个独立的字典中,方便取用。
'''__new__必须要有返回值,返回实例化出来的实例,
        这点在自己实现__new__时要特别注意,
        可以return父类__new__出来的实例,
        或者直接是object的__new__出来的实例'''
class OrmMetaclass(type):
    def __new__(cls, class_name,class_base,class_dict):
        #class_base:类名(表名)class_base:基类/父类 class_dict:类的名称空间
        
        #过滤Models类,如果输入的是Models类,直接将类返回
        if class_name == 'Models':
            return type.__new__(cls,class_name,class_base,class_dict)
        table_name = class_dict.get('table_name',class_name)
        #获取table_name如果table_name不存在,就获取class_name
        primary_key = None
        定义一个空的字典,专门用来存放字段对象
        mappings = {}
        
        遍历名称空间所有的属性
        print(class_dict)
        '''
               {'__module__': '__main__', '__qualname__': 'User',
                'user_id': <__main__.IntegerField object at 0x000001C3E13FA7B8>,
                 'user_name': <__main__.StringField object at 0x000001C3E13FA7F0>,
                  'pwd': <__main__.StringField object at 0x000001C3E13FA828>}
        '''
        #这里的每一个字段属性都是对应的字段类实例化出的对象
        #key是对象名,value是对象地址

        #遍历类的名称空间的所有属性
        for key,value in class_dict.items():

            #判断value是否是Field 实例化的对象,
            # Field是所有字段的父类
            if isinstance(value,Field):

                #将属性和名称放入mappings字典里
                mappings[key] = value

                if value.primary_key:
                    #判断实例化出来的字段的主键属性是否为true

                    #如果primary_key有值,则已经有一个字段的primary_key属性
                    #设置为了True
                    if primary_key:
                        raise TypeError('只能有一个主键')

                    #如果该字段是主键,就将主键名赋值给primary_key
                    primary_key = value.name

        for key in mappings.keys():
            class_dict.pop(key)

        #给类的名称空间添加表名
        class_dict['table_name'] = table_name

        #给类的名称空间添加主键名
        class_dict['primary_key'] = primary_key

        #给类的名称空间添加mapping字典,字典中拥有所有的字段属性
        class_dict['mappings'] = mappings
        return type.__new__(cls,class_name,class_base,class_dict)
posted @ 2019-11-04 20:27  ylpb  阅读(393)  评论(0编辑  收藏  举报