zoukankan      html  css  js  c++  java
  • 1105 python利用元类实现ORM框架

    ROM框架

    ORM对象映射关系
    类名       --       表名
    对象       --       记录
    对象.属性  ---      字段
    

    1.定义表字段的类型

    2.创建元类,限制表类的创建

    '''
    元类需要处理的问题:
        1.一张表必须要有一个表名。
        2.给数据表类,强制必须要有一个主键,主键必须是唯一的。
        3.将数据表中,所有的字段对象,都存放在一个独立的字典中
            存不是目的,目的是为了取值方便。
    '''
    
    1. 过滤Models类,models类中,什么都不做,将类原路返回。

    2. 一张表必须有表名,如果没有则将类名做表名

      1. 表名
        主键名
        定义一个空字典,用来存放字段对象
        
    3. 遍历名称空间的属性判断主键的存在与否

    4. 节省资源:mappings字典中与原名称空间中有属性重复,提出属性
      

    3.创建用户表类,继承dict与元类,以解决表类中数据的不一致传参,和主键的限制

    '''
    ORM对象映射关系
    类名       --       表名
    对象       --       记录
    对象.属性  ---      字段
    
    '''
    
    
    # 1.创建字段的类型
        # 创建字段时,create table id int primary key default
    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
        # 创建int类型
    class IntegerField(Field):
        def __init__(self,name,column_type='int',primary_key=False,default=0):
            super().__init__(name,column_type,primary_key,default)
    
        # 创建str类型
    class StringField(Field):
        def __init__(self,name,column_type='varchar(64)',primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)
    
    
    '''
    元类需要处理的问题:
        1.一张表必须要有一个表名。
        2.给数据表类,强制必须要有一个主键,主键必须是唯一的。
        3.将数据表中,所有的字段对象,都存放在一个独立的字典中
            存不是目的,目的是为了取值方便。
    '''
    
    # 2.创建元类并限制表类的创建条件
    class Mymetaclass(type):
        def __new__(cls,class_name,class_base,class_dict):
            # print(class_name)   # 类名
            # print(class_base)   # 父类
            # print(class_dict)   # 类的名称空间(属性)
    
            if class_name == 'Models':
                # models类中,什么都不做,将类原路返回。
                return type.__new__(cls, class_name, class_base, class_dict)
            # 1.表名
            table_name = class_dict.get('table_name',class_name)
            # 2.主键名
            key = None
            # 3.定义一个空字典,用来存放字段对象
            mappings = {}
    
            # 遍历名称空间中的所有属性
            for k ,v in class_dict.items():
                # print(k,v)  # 除了有字段,还有其他字段以外的属性
                '''
    __module__ __main__
    __qualname__ User
    user_id <__main__.IntegerField object at 0x00000173FF5CFD30>
    user_name <__main__.StringField object at 0x00000173FF5CFCF8>
    pwd <__main__.StringField object at 0x00000173FF5CFCC0>'''
                
                # 过滤字段对象以外的内容
                if isinstance(v,Field):
                    mappings[k] = v  # 保存至字段字典中
                    # print(k,v)
    '''user_id <__main__.IntegerField object at 0x0000020CDCDAFD30>
    user_name <__main__.StringField object at 0x0000020CDCDAFCC0>
    pwd <__main__.StringField object at 0x0000020CDCDAFCF8>'''
                    # print(mappings)
    '''{'user_id': <__main__.IntegerField object at 0x00000212F5BFFD30>}
    {'user_id': <__main__.IntegerField object at 0x00000212F5BFFD30>, 'user_name': <__main__.StringField object at 0x00000212F5BFFCF8>}
    {'user_id': <__main__.IntegerField object at 0x00000212F5BFFD30>, 'user_name': <__main__.StringField object at 0x00000212F5BFFCF8>, 'pwd': <__main__.StringField object at 0x00000212F5BFFCC0>}'''
    
                    # 判断字段对象primary_key是否为True,然后再判断设置的key有没有值
                    if v.primary_key:
                        # 再判断初始的key是否有值
                        if key:
                            # 如果有值代表有两个主键
                            raise TypeError('只能有一个主键')
    
                        # 若对象字段主键不存在,则给key赋值,说明主键字段有值了
                        key = v.name
                        # print(v.name)  # user_id
    
                    # 判断是否有主键
                    if not key:
                        raise TypeError('必须有一个主键')
            #
            # # 判断是否有主键
            # if not key:
            #     raise TypeError('必须有一个主键')
    
    
            # 节省资源:mappings字典中与原名称空间中有属性重复,提出属性
            for k in mappings.keys():    # 遍历所有的keys,去除class_dict 中的相同值
                class_dict.pop(k)       # pop 删除dict中对应k的键值对
    		print(class_dict)
           '''{'__module__': '__main__', '__qualname__': 'User'}'''
            # 给类的名称空间添加表名
            class_dict['table_name'] = table_name
            # 给类的名称空间添加主键名
            class_dict['primary_key'] = key
            # 给类的名称空间添加一个mappings字典,字典拥有所有字段属性
            class_dict['mappings'] = mappings
    		# print(class_dict)
            '''
    {'__module__': '__main__', '__qualname__': 'User', 'table_name': 'User', 'primary_key': 'user_id', 'mappings': {'user_id': <__main__.IntegerField object at 0x000001D5393DFD30>, 'user_name': <__main__.StringField object at 0x000001D5393DFCC0>, 'pwd': <__main__.StringField object at 0x000001D5393DFCF8>}}'''
            # 返回__new__创建的对象
            return type.__new__(cls,class_name,class_base,class_dict)
    
    
    
    # 3.创建用户表类,继承dict与元类,以解决表类中数据的不一致传参,和主键的限制
    class Models(dict,metaclass=Mymetaclass):
    
        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='user_name')
        pwd = StringField('pwd')
        pass
    
    if __name__ == '__main__':
        User()
        # print(User.__dict__)
    

    model中定义 查找,插入,更新,删除的方法

    '''
    ORM对象映射关系
    类名       --       表名
    对象       --       记录
    对象.属性  ---      字段
    
    '''
    
    from MySQL_control import MySQL
    
    # 1.创建字段的类型
        # 创建字段时,create table id int primary key default
    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
        # 创建int类型
    class IntegerField(Field):
        def __init__(self,name,column_type='int',primary_key=False,default=0):
            super().__init__(name,column_type,primary_key,default)
    
        # 创建str类型
    class StringField(Field):
        def __init__(self,name,column_type='varchar(64)',primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)
    
    
    '''
    元类需要处理的问题:
        1.一张表必须要有一个表名。
        2.给数据表类,强制必须要有一个主键,主键必须是唯一的。
        3.将数据表中,所有的字段对象,都存放在一个独立的字典中
            存不是目的,目的是为了取值方便。
    '''
    
    # 2.创建元类并限制表类的创建条件
    class Mymetaclass(type):
        def __new__(cls,class_name,class_base,class_dict):
            # print(class_name)   # 类名
            # print(class_base)   # 父类
            # print(class_dict)   # 类的名称空间(属性)
    
            if class_name == 'Models':
                # models类中,什么都不做,将类原路返回。
                return type.__new__(cls, class_name, class_base, class_dict)
            # 1.表名
            table_name = class_dict.get('table_name',class_name)
            # 2.主键名
            primary_key = None
            # 3.定义一个空字典,用来存放字段对象
            mappings = {}
    
            # 遍历名称空间中的所有属性
            for k ,v in class_dict.items():
                # print(k,v)  # 除了有字段,还有其他字段以外的属性
                # 过滤字段对象以外的内容
                if isinstance(v,Field):
                    mappings[k] = v  # 保存至字段字典中
                    # print(k,v)
                    # print(mappings)
    
                    # 判断字段对象primary_key是否为True,然后再判断设置的key有没有值
                    if v.primary_key:
                        # 再判断初始的key是否有值
                        if primary_key:
                            # 如果有值代表有两个主键
                            raise TypeError('只能有一个主键')
    
                        # 若对象字段主键不存在,则给key赋值,说明主键字段有值了
                        key = v.name
                        # print(v.name)
    
                    # # 判断是否有主键
                    # if not primary_key:
                    #     raise TypeError('必须有一个主键')
    
    
    
    
            # 节省资源:mappings字典中与原名称空间中有属性重复,提出属性
            for k in mappings.keys():    # 遍历所有的keys,去除class_dict 中的相同值
                class_dict.pop(k)       # pop 删除dict中对应k的键值对
            # print(class_dict)
    
            # 判断是否有主键
            if not primary_key:
                raise TypeError('必须有一个主键')
    
            # 给类的名称空间添加表名
            class_dict['table_name'] = table_name
            # 给类的名称空间添加主键名
            class_dict['primary_key'] = primary_key
            # 给类的名称空间添加一个mappings字典,字典拥有所有字段属性
            class_dict['mappings'] = mappings
            # print(class_dict)
    
            # 返回__new__创建的对象
            return type.__new__(cls,class_name,class_base,class_dict)
    
    
    
    # 3.创建用户表类,继承dict与元类,以解决表类中数据的不一致传参,和主键的限制
    class Models(dict,metaclass=Mymetaclass):
    
        def __getattr__(self, item):
            return self.get(item)
    
        def __setattr__(self, key, value):
            self[key] = value
    
    
        # 查找,插入,更新,删除的方法
    
        # 查找    (将查询封装为类方法,由类直接调用查找)
        @classmethod
        def select(cls,**kwargs):   # 利用**kwargs打散关键字参数为字典
            # print(kwargs)
            # 调用Mysql拿到对象 mysql
            mysql = MySQL()
            # 查询指令有全部查询,与按条件where查询
            # 1.sql = 'select * from table;'
            if not kwargs:      # 如果没有where 条件的指令走这里
                sql = 'select * from %s ' % cls.table_name  # 通过类方法获得类名
                res = mysql.my_select(sql)    # 调用client的select方法
    
            else:  #  有值的话证明需要where 条件查询
                # 2.sql = 'select * from table where 条件;'
                key = list(kwargs.keys())[0]    #获取传入的参数(list类型转换为列表)取值
                # print(list(kwargs.keys()))
                value = kwargs.get(key)         # 按key取值获取传入的value
                # 查询的条件语句
                sql = 'select * from %s where %s=? ' % (cls.table_name,key)
                # 使用%s 替换?,  ? 的作用是防止用户输入时的sql注入
                sql = sql.replace('?','%s')
    
                # 拿到mysql的游标,提交sql语句
                res = mysql.my_select(sql,value)
    
            # d = {'id':1,'name':2}   ** 可以将字典打散传入函数中为关键字参数
            return [cls(**d) for d in res]  #return出去的就具有.属性的方法取值
    
    
        # 插入     User(传关键字参数) ---》 user_obj.insert(user_obj)
        def insert(self): # 对象调用传入参数,用户输入的是值,字段名设置好的
            # 调用mysql 拿到对象
            mysql = MySQL()
            # insert into table (字段名) values (值);
            # 其中 字段可能有多个值,传值也有多个,所以用%s进行替代
    
            # 存储所有的字段名
            keys = []
            # 存字段对应的值
            values = []
            # 存放?号的,有几个字段,就存在几个?号,替换条件'???'
            args = []
    
            # mappings存放的是字段的对象,通过items 循环取出
            for k,v in self.mappings.items():
                # print(k,v)  # user_id = IntegerField(name='user_id',primary_key=True)
                # 过滤掉主键,因为主键是自增的,不需要添加
                if not v.primary_key:
                    # 存表中除了主键以外的字段名
                    keys.append(v.name)     # k == v.name
    
                    # 存表中除了主键以外的字段值,若值没有,则使用默认值
                    values.append( getattr(self , v.name , v.default) ) # 通过反射才能找到传的值
                    # 这里values获取的是用户传来的所有关键字参数的值,也就是修改的值.
    
                    # 存放?号,有几个字段,就存几个?号,用来替换用户输入的字符
                    args.append('?')
            # print(keys)
            # print(values)
            # print(args)
            # sql: insert into table_name(v1, v2, v3) values(?, ?, ?)
            sql = 'insert into %s(%s) values (%s);' % (
                self.table_name,
                ','.join(keys),
                ','.join(args)
            )
    
            sql = sql.replace('?','%s')
            # sql: insert into table_name(v1, v2, v3) values(%s, %s, %s)
    
            # 封装一个my_execupt方法,用于发送指令
            mysql.my_execupt(sql,values)
            # mysql.cursor.execute(sql, values)
    
    
        # 更新
        def sql_update(self):
            # 默认使用主键当做更新的查询条件 primary_key的名字是主键名,主键
            mysql = MySQL()
            # 字段名
            keys = []
            # 字段值
            values = []
            # 主键  id = pk --- id = 1 这样的
            primary_key = None      # 主键可能不一样,所以需要主键名
    
            # 获取所有的字段名以及字段的对象
            for k,v in self.mappings.items():
                # 判断如果是主键的话进行赋值操作
                if v.primary_key:
                    # 定义拼接主键为: 具有主键的名(id) + '=' + 利用反射id获取的主键的对应值
                    primary_key = v.name + '=%s' % getattr(self,v.name)
                    print(v.name)
                    print(getattr(self,v.name))
                # 其他的都是需要修改的值(除了主键剩下的都是字段了)
                else:
                    # 获取所有的字段的名字
                    keys.append(v.name + '=?')  # ? 则是用户输入的值,传值时需要替换,再传入values
                    # 通过getattr 反射获得所有字段的值
                    values.append(getattr(self,v.name))
    
            # sql: update table set k1=?, k2=? where id=pk; #
            # sql = update table set key=value, k2=v2 where user_id = 1;(这里将主键固定为条件)
            # 将where 条件后的'主键字段=主键值' 替换为一个 %s
            sql = 'update %s set %s where %s' % (
                self.table_name,
                ','.join(keys),
                primary_key
            )
            # 替换"?"
            sql = sql.replace('?','%s')
            # 调用mysql中的my_execupt上传方法
            mysql.my_execupt(sql,values)
    
    
    
        # 用户表
    class User(Models):
        # 类名就是表名,类的属性(对象)就是字段.
        user_id = IntegerField(name='user_id',primary_key=True)
        user_name = StringField(name='user_name')
        pwd = StringField('pwd')
        pass
    
    
    
    if __name__ == '__main__':
    
        # 增  orm_insert
        # User表 ---> 添加字段对应的数据
        obj = User(user_name='小明', pwd='123')
        obj.insert()
    
    
        # 查询
        # res1 = User.select()   # 查询所有
        # print(1,res1)
        # res2 = User.select(user_name='小明')
        # print(2,res2)
    
    
        # 改
        # obj = User.select()
        # print(obj.user_name)
        # obj.user_name = '小红'
        # obj.sql_update()
    
    
  • 相关阅读:
    java中的io笔记
    Android面试
    使用org.apache.commons.net.ftp包开发FTP客户端,实现进度汇报,实现断点续传,中文支持
    linux基本命令学习
    jmeter简单的压力测试
    linux配置tomcat项目包
    linux配置jdk环境
    mysql插入大量数据
    jmeter压测mysql数据库
    selenium+python自动化元素定位
  • 原文地址:https://www.cnblogs.com/fwzzz/p/11802226.html
Copyright © 2011-2022 走看看