ORM分析

orm目的是为了可以实现表与类的映射:表中有的数据,类中全部都有对应,而且拥有 对象.属性 可以获取到数据的方法
# ORM:对象关系映射
类 >>> 数据库的一张表
对象 >>> 表的一条记录
对象.属性 >>> 记录中某一个字段对应的值



# 首先映射字段
# 表的字段中需要有的属性有 字段名|字段数据类型|是否为主键|默认值
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

# 字段类型分为好几种,为了创建字段时的方便,可以重写init方法去给定某些数据类型字段的默认值
# 定义varchar类型的字段
class StrField(Field):
    def __init__(self, name, column_type='varchar(32)', primary_key=False,
                default=None):
        super().__init__(name, column_type, primary_key, default)
        
# 定义int类型的字段
class IntField(Field):
    def __init__(self, name, column_type='int', primary_key=False,
                default=None):
        super().__init__(name, column_type, primary_key, default)
        
# 这样子就可以通过实例化出来的对象 . 出 字段名,字段类型,是否为主键,默认值
# 这个类就表示了 数据库中表的一个个字段,字段叫啥名字,字段类型是啥,是否是主键,默认值是啥
# 想要实现==>>无论传什么 不同名 不同个数 的参数都可以完成实例化对象
字典类 实例化字典时的传参方式即
dic = dict(name='zdc',pwd=123)
dic1 = dict(age=21)
print(dic)  # {'name': 'zdc', 'pwd': 123}
print(dic1)  # {'age': 21}
# 创建一个可以自由传参实例化对象的类
class Models(dict):  # 继承字典类,目的是为了继承他的__init__方法:自由传值 实例化对象
    def __init__(self, **kwargs):
        # 将Models拿到的位置参数拿过来,给dict的__init__来实例化对象
        super().__init__(self, **kwargs)  
        # 此时实例化出的是Models对象,但其实是披着Models皮 的dict对象,他的取值方法还是 model[key], 而不是对象的 model.key
    
    def __getattr__(self,item):  # 此方法在获取对象中没有的属性时会触发
        return self.get(item, '没有该键')
    	# 触发时说明类中没有该属性,但是这个伪字典对象中是有这个值的,所以我们就 重写取值方法,写一个字典的取值方法替代之
        # 字典的两种取值方式: dic[key]  |  dic.get(key,取不到时返回的默认值)
        # 后者不会报错,取不到即返回指定默认值,不指定则为None
    
    # 此时 __getattr__就实现了 对象.属性 的取值方式
	
    
    # 现在还需要一个更改伪字典中value的方法
    def __setattr__(self, key, value):  # 对象.属性 = xxx 时触发
        self[key] = value  # 此即为字典的改值方法
# 在表创建时就在表中加入 表名|主键值|除主键字段外的其他所有字段 
# 元类:创建类的类
	用元类拦截类的创建过程,在类创建之前,把 表名|主键值|除主键字段外的其他所有字段  塞给这个要创建的类, 这样的一个类创建出来才能合法的映射出一张表
# 自定义元类重写__new__:
class MyMetaClass(type):
    def __new__(cls,*args,**kwargs):
        print(cls)  # <class '__main__.MyMetaClass'>
        print(args)
        # ('User', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'User', '__init__': <function User.__init__ at 0x000000000244AAE8>})
        print(kwargs)  # {}
class User(object,metaclass=MyMetaClass):
    def __init__(self,name):
        self.name = name
# 由上可见,args中有: 类名 | 类的所有父类 | 类的名称空间中的属性
# 而kwargs则是空的,那么我们可以写作下面一个版本:
        
# 将args解耦版:
class MyMetaClass(type):
    def __new__(cls, class_name,class_bases,class_attrs):
        print(cls)  # <class '__main__.MyMetaClass'>
        print(class_name)  # User
        print(class_bases)  # (<class 'object'>,)
        print(class_attrs)
        # {'__module__': '__main__', '__qualname__': 'User', '__init__': <function User.__init__ at 0x00000000027EAAE8>}


class User(object, metaclass=MyMetaClass):
    def __init__(self, name):
        self.name = name
        
# 至此我们已经拿到了下面User类的 类名 | 类的所有父类 | 类的名称空间中的属性
# 此前我们说要 在表创建时就在表中加入 表名|主键值|除主键字段外的其他所有字段  
# 则 我们需要表创建时 将 表名|主键值|除主键字段外的其他所有字段  塞到其名称空间中去
# 看以下完整版自定义元类
class MyMetaClass(type):
    def __new__(cls, class_name, class_bases, class_attrs):  # 参数解耦,因为要我那个class__attr中塞入属性,其中的class__attr为键值对的字典
        # 首先筛选出自定义属性
        if class_name == 'Models':
            return type.__new__(cls, class_name, class_bases, class_attrs)

        # 接下来就是得到  表名|主键值|除主键字段外的其他所有字段 
        # 只有先得到才能塞进去
        table_name = getattr(cls, 'table_name', class_name)
        primary_key = None  # 这个primary_key只是为了后面验证表的主键合法性,以及保存主键字段名
        mappings = {}  # 用来整合保存 IntegerField(),StringField(),最后再一次性打包给class_attrs

        for k, v in class_attrs.items():
            # k: id,name  v:IntegerField(),StringField()  加了括号就是一个个表对象
            if isinstance(k, Field):
                mappings[k] = v
                if v.primary_key:
                    if primary_key:
                        raise TypeError('一张表只能有一个主键')
        if not primary_key:
            raise TypeError('必须要有一个主键')
        for k in mappings:
            class_attrs.pop(k)

        # 将我们获得到的 表名|主键值|除主键字段外的其他所有字段 放入class_attr名称空间中
        class_attrs['table_name'] = table_name
        class_attrs['primary_key'] = primary_key
        class_attrs['mappings'] = mappings
        return type.__new__(cls, class_name, class_bases, class_attrs)
    
    
    # 接下来在Models类中的 写上调用sql语句执行命令的方法
from orm_pool.mysql_singleton import MySQL
import os
# 将刚才写了 任意传值实例化|点取值|点=改值 的Models拿来继续完善

class Models(dict):
    def __init__(self, **kwargs):
        super().__init__(self, **kwargs)  

    def __getattr__(self,item):
        return self.get(item, '没有该键')

    def __setattr__(self, key, value):
        self[key] = value
    # 至此 写了三个'内置方法'
    
    # select sql方法
    # select 操作的最小单位是表,而表即我们的类,记录即我们的对象,所以将其做成类方法
    # 需要的参数为 非必要的where约束条件
    # select * from 表名 where X=X
    @classmethod
    def select(cls,**kwargs):
        ms = MySQL()  # 得到sql的execute发送指令接口
        if not kwargs:  # select * from 表名
            sql = "select * from %s" % cls.table_name
            res = ms.select(sql)
        else:  # select * from 表名 where X=X
            # 只接收一个约束条件
            k = list(kwargs.keys())[0]
            v = kwargs.get(k)
            
            sql = "select * from %s where %s=?" % (cls.table_name, k)
            sql = sql.replace('?','%s')
            res = ms.select(sql, v)
        # res = [{name:'zdc'}, {age:'13}, ...]
        # cls(name='zdc', age='13', ...)  # 关键字参数形式
    	return [cls(**r) for r in res]  # **将字典打散
    
    # insert sql方法
    # insert 操作的最小单位是记录,即对象,所以做成对象方法
    def insert(self):  # 不需要参数,参数都可以从self.mappings中获得
        ms = MySQL()
        # insert into 表名 (字段1, 字段2, ...) values (字段1的值, 字段2的值, ...)
        # 需要一个记录 非主键的其他所有字段
        fields = []
        # 需要一个记录 字段后值的字符替换个数
        args = []
        # 需要一个记录 values的
        values = []
        
        # 筛选出非主键的字段
        for k, v in self.mappings.items():
            if not v.primary_key:
                fields.append(v.name)
                args.append('?')
                values.append(getattr(self,v.name,v.default))
        
        sql = "insert into %s (%s) values (%s)" % (self.table_name, ','.join(fields), ','.join(args))
        sql = sql.replace('?','%s')
        ms.execute(sql, values)
        
    # update sql方法
    # update 操作的最小单位是记录,即对象,所以做成对象方法
    def update(self):
        ms = MySQL()
        # update 表名 set 字段1=XXX, 字段2=YYY,... where id=Z
        # 需要一个存放非主键的字段
        fields = []
        # 需要一个主键值
        pr = None
        # 需要一个记录values
        values = []
        
        # 筛选出非主键的字段,并记录主键值
        for k,v in self.mappings.items():
            if v.primary_key == self.mappings['primary_key']:
                pr = getattr(self, v.name)
            else:
                fields.append(v.name+'=?')
                values.append(getattr(self,v.name,v.default))
            
        
        sql = "update %s set %s where %s=%s" % (self.table_name, ','.join(fields), self.primary_key, pr)
        sql = sql.replace('?','%s')
        ms.execute(sql, values)
posted @ 2019-05-24 10:28  输诚  阅读(123)  评论(0编辑  收藏  举报