zoukankan      html  css  js  c++  java
  • python之ORM的应用

    '''
    ORM 对象关系映射  --->映射到数据表的一条条记录
    类名 ---> 表名
    对象 ---> 一条记录
    对象.属性 ---> 字段
    '''
    from day38.mysql_control import MySQL
    
    # 1.创建字段的类型 对应数据表中一个个字段的创建规范
    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 StringField(Field):  #varchar
        def __init__(self,name,column_type='varchar(64)',primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)  #继承父类
    
    class IntegerField(Field):  #int
        def __init__(self,name,column_type='int',primary_key=False,default=0):
            super().__init__(name,column_type,primary_key,default)
    
    # 自定义元类:
    # 解决三件事情
    # 1、保证一张表必须要有表名  2、保证一张表中只能有一个主键
    # 3、将所有 “字段名” 与 “字段对象” 添加到一个独立的字典中(mappings),
    # 以key(字段名):value(字段对象) , 添加到类的名称空间中,方便后期使用
    
    # 控制表类的创建  ---> 控制表创建的过程
    class OrmMetaClass(type):
        # __call__ ---> __new__ ----> type.__new__() ---> obj ---> Models
        #只要定义类就会触发__new__, 因为类也是对象,OrmMetaClass() ---> Models类对象 、User类对象
        def __new__(cls,class_name,class_bases,class_attr):
            # print(f'类名: {class_name}')
            # print(f'基类: {class_bases}')
            # print(f'类的名称空间: {class_attr}')
    
            #1、过滤Models类,因为只需要控制user类,不需要控制Models类
            if class_name =='Models':
                # 将models类的类名、基类、名称空间原路返回
                return type.__new__(cls,class_name,class_bases,class_attr)
    
            # 2、获取 table 表名,若自定义则获取,没有则默认使用类名
            # dict.get(key) ---> key若有则返回对应的值,若没有则返回默认值 class_name就是默认值
            # 将类名当做表名
            table_name = class_attr.get('table_name',class_name)
            # print(table_name)
            # 主键值: 主键名为 字段名, 比如 主键是 id字段 ---》 id就是主键的名字
            primary_key = None
            mappings = {}   # 存放字段名与字段对象的字典
    
            # 3、保证一张表只能有一个唯一的主键
            for k,v in class_attr.items():     # 循环遍历类的名称空间
                # print(k,v)
                #将字段以外的属性过滤调掉
                if isinstance(v,Field): #判断v是否是Field的一个实例
                    # print(k,v)
                    # print(v.__dict__)
    
                    # 4、将所有 “字段名” 与 “字段对象” 添加到一个独立的字典中(mappings)
                    mappings[k] = v
                    if v.primary_key:  # 判断字段对象如果有 主键primary_key, 则为primary_key 变量赋值
                        if primary_key:
                            raise TypeError('一张表只能有一个主键')
                        primary_key = v.name
            # print(mappings)
            # 5、过滤掉类名称空间中重复的字段属性
            for key in mappings.keys():
                class_attr.pop(key)
            if not primary_key:
                raise TypeError('必须要有一个主键!!!')
            # 6、给类的名称空间,添加table_name, primary_key,mappings属性;
            class_attr['table_name'] = table_name
            class_attr['primary_key'] = primary_key
            class_attr['mappings'] = mappings
            # print(class_attr)
            return type.__new__(cls,class_name,class_bases,class_attr)
    
    
    # 创建数据表类
    #让models类继承字典类,所有子类继承Models类就等于继承了dict类,调用User类例触发dict类的__init__
    class Models(dict,metaclass=OrmMetaClass):   #创建一个父类,用继承减少写__init__的次数
        #在通过 “对象.属性” 获取属性,若 “属性没有” 时触发。
        def __getattr__(self, item):
            # print(item,'调用对象没有的属性时触发')  #item --->对象.属性
            # 将字典中key对应的值返回给User的对象
            return self.get(item)    #dict_obj.get(key) = 属性值
        # 在对象.属性=值时,触发
        def __setattr__(self, key, value):#self--->user对象,即字典对象  key---> 对象.属性名  value ---》 属性值
            self[key]=value   # 给字典添加键值对的方式
    
        # 查询数据
        @classmethod
        def select_data(cls,**kwargs): # name=tank ---> {'name': "tank"}
            mysql_obj = MySQL()
            if not kwargs:  #若kwargs为False代表没有查询条件
                #1.查所有
                #sql = 'select * from 表名'
                sql = f'select * from {cls.table_name}'  #cls-->User
                res = mysql_obj.select(sql)
                # print(res)
                return [cls(**r) for r in res]
            else:
                field_name = list(kwargs.keys())[0]   #获取字段名 #keys返回的是对象
                field_value = kwargs.get(field_name)  #获取字段值
                #2.根据条件查询
                #select * from 表名 where 字段=字段值
                sql = 'select * from %s where %s=?' %(cls.table_name,field_name)
                sql = sql.replace('?','%s')
                res = mysql_obj.select(sql,field_value)
                # print(res)  #[{},{}]
                # ** ---> [{key: values}, {}]  ---> [User(key=value)]
                return [cls(**r) for r in res]   #---》[obj,obj....]
    
        #插入数据
        def insert_data(self):  #self--->user_obj
            mysql_obj = MySQL()
            # sql: insert into 表名(字段名1, 字段名2) values(字段值1, 字段值2);
            # 1.表名  ---》 self.table_name
            # 2.字段名与字段值---》 mappings
            field_names = []  # 存放字段名的列表
            field_values = []  # 存放字段值的列表
            replace_list = []  # 设置一个替换值的列表   [?, ?]
            for k,v in self.mappings.items():
                field_names.append(v.name)
                field_values.append(getattr(self,v.name,v.default))  #self-->user_obj
                replace_list.append('?')
    
    
            sql = 'insert into %s(%s) values(%s)' %(
                self.table_name,",".join(field_names),",".join(replace_list))
            sql = sql.replace('?','%s')
    
            mysql_obj.execute(sql,field_values)
    
        #更新数据
        def update_data(self):
            mysql_obj = MySQL()
            #sql : update 表名 set 字段名=字段值 where pk=主键值;
            #主键值
            pk = None
            filed_names = [] # [字段名, 字段名, 字段名, ]
            filed_values = []  # [字段值, 字段值,字段值,]
            for k, v in self.mappings.items():
                if v.primary_key:   # 判断mappings中哪一个字段是主键
                    #若是主键,则获取主键值
                    pk = getattr(self,v.name)
                else:
                    filed_names.append(v.name+'=?')  # 添加字段名
                    filed_values.append(getattr(self,v.name))  # 添加字段值
    
            # sql: update User set 字段名=字段值, 字段名=字段值
            # sql: update User set username=?, password=? where pk=1;
            sql = 'update %s set %s where %s=%s' %(
                self.table_name,
                ','.join(filed_names),
                self.primary_key,
                pk
            )
            sql = sql.replace('?','%s')
            mysql_obj.execute(sql,filed_values)
    
    
    class User(Models):   #用户表类
        # table_name = 'user_info'
        # IntegerField字段类中的name属性 必须与User表中类属性同名
        id = IntegerField(name='id',primary_key=True)
        username = StringField(name='username')
        password = StringField(name='password')
    
    # class Movie(Models):
    #     pass
    
    # user = User(name='tank')  #user本质上就是一个字典对象
    # print(user.sex)
    
    if __name__ == '__main__':
    
        #查询数据
        # user_obj = User.select_data()[0]
        user_obj = User.select_data(username='tank')[0] #select_data()括号中不传参指的是查询所有数据
        print(user_obj)
        # # print(user_obj.name)
    
        # #插入数据
        # user_obj = User(username='tank_dsb',password=123)
        # user_obj.insert_data()
    
        #更新数据
        # 获取jason对象
        # user_obj = User.select_data(username='tank_dsb')[0]
        # print(user_obj)
        # # 修改对象中属性的值
        # user_obj.username = '大弟弟_DDSB'
        # # 然后再调用update_data方法更新到数据库中
        # user_obj.update_data()
    
    
    
    
    '''
    问题1:解决代码冗余问题,比如有100张表,需要写100次__init__
    解决1:定义一个父类,继承父类
    
    问题2:无法通过预测每一张表中的字段是什么,无法通过父类的__init__解决问题
    解决2:通过继承字典,内部的__init__,可以接受“任意个数”的关键字参数。
    
    问题3:继承字典的类实例化的对象,无法通过对象.属性的方式存取值
    解决3:通过__setattr__,__getattr__实现,让字典对象与普通对象一样,并具备字典原有的特性
    '''
    
  • 相关阅读:
    POJ 2774 Long Long Message
    Jmeter学习——5
    Jmeter学习——4
    Jmeter学习——10
    Jmeter学习——9
    使用 JMeter 完成常用的压力测试 [转]
    Jmeter学习——3
    Jmeter学习——7
    Jmeter学习——11
    Jmeter学习——8
  • 原文地址:https://www.cnblogs.com/baohanblog/p/12144116.html
Copyright © 2011-2022 走看看