• 自己动手写一个简易对象关系映射,ORM(单例版和数据库池版)


    准备知识

    DBUtils模块  <<-----重点

    DBUtils是Python的一个用于实现数据库连接池的模块
    
    此连接池有两种连接模式:
    
        DBUtils提供两种外部接口:
        PersistentDB :提供线程专用的数据库连接,并自动管理连接。
        PooledDB :提供线程间可共享的数据库连接,并自动管理连接。
    from DBUtils.PooledDB import PooledDB
    import pymysql
    
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
        maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
        maxshared=3,  # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
        blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
        setsession=[],  # 开始会话前执行的命令列表。
        ping=0,
        # ping MySQL服务端,检查是否服务可用。
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123456',
        database='youku',
        charset='utf8',
        autocommit = True
    )
    DBUtils,配置模板
    def func():
        ...
        conn = POOL.connection()
        ...

    元类概念回顾(>>传送门点这里<<)

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 2018/05/17 8:25
    # @Author  : MJay_Lee
    # @File    : 列表推导式.py
    # @Contact : limengjiejj@hotmail.com
    egg_list = []
    for i in range(10):
        egg_list.append('egg%s' % i)
    
    print(egg_list)
    # ['egg0', 'egg1', 'egg2', 'egg3', 'egg4', 'egg5', 'egg6', 'egg7', 'egg8', 'egg9']
    
    egg_list2 = ['egg%s' % i for i in range(10)]
    print(egg_list2)
    列表推导式
    class Foo:
        x=1
        def __init__(self,y):
            self.y=y
    
        def __getattr__(self, item):
            print('----> from getattr:你找的属性不存在')
    
    
        def __setattr__(self, key, value):
            print('----> from setattr')
            # self.key=value #这就无限递归了,你好好想想
            # self.__dict__[key]=value #应该使用它
    
        def __delattr__(self, item):
            print('----> from delattr')
            # del self.item #无限递归了
            self.__dict__.pop(item)
    
    #__setattr__添加/修改属性会触发它的执行
    f1=Foo(10)
    print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
    f1.z=3
    print(f1.__dict__)
    
    #__delattr__删除属性的时候会触发
    f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
    del f1.a
    print(f1.__dict__)
    
    #__getattr__只有在使用点调用属性且属性不存在的时候才会触发
    f1.xxxxxx
    __getattr__,__setattr__

    补充:

    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __getattr__(self, item):
            print('执行的是我')
            # return self.__dict__[item]
        def __getattribute__(self, item):
            print('不管是否存在,我都会执行')
            raise AttributeError('哈哈')
    
    f1=Foo(10)
    f1.x
    f1.xxxxxx
    
    #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
    区分__getattr__,__getattribute__

     反射(update和save两个功能代码里,拼接SQL语句时,给参数赋值时时需要用上): 

    def getattr(object, name, default=None): # known special case of getattr
        """
        getattr(object, name[, default]) -> value
    
        Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
        When a default argument is given, it is returned when the attribute doesn't
        exist; without it, an exception is raised in that case.
        """
        pass
    
    
    def setattr(x, y, v): # real signature unknown; restored from __doc__
        """
        Sets the named attribute on the given object to the specified value.
    
        setattr(x, 'y', v) is equivalent to ``x.y = v''
        """
        pass
    
    
    def delattr(x, y): # real signature unknown; restored from __doc__
        """
        Deletes the named attribute from the given object.
    
        delattr(x, 'y') is equivalent to ``del x.y''
        """
        pass
    getattr及其它相关属性
    class BlackMedium:
        feature='Ugly'
        def __init__(self,name,addr):
            self.name=name
            self.addr=addr
    
        def sell_house(self):
            print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name)
        def rent_house(self):
            print('%s 黑中介租房子啦,傻逼才租呢' %self.name)
    
    b1=BlackMedium('万成置地','回龙观天露园')
    
    #检测是否含有某属性
    print(hasattr(b1,'name'))
    print(hasattr(b1,'sell_house'))
    
    #获取属性
    n=getattr(b1,'name')
    print(n)
    func=getattr(b1,'rent_house')
    func()
    
    # getattr(b1,'aaaaaaaa') #报错
    print(getattr(b1,'aaaaaaaa','不存在啊'))
    
    #设置属性
    setattr(b1,'sb',True)
    setattr(b1,'show_name',lambda self:self.name+'sb')
    print(b1.__dict__)
    print(b1.show_name(b1))
    
    #删除属性
    delattr(b1,'addr')
    delattr(b1,'show_name')
    delattr(b1,'show_name111')#不存在,则报错
    
    print(b1.__dict__)
    类与对象的四个操作属性示例

    操作类与对象的属性的补充:

    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    del f1
    print('------->')
    
    #输出结果
    执行我啦
    ------->
    
    ----------------------以下是另一种情况
    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    # del f1
    print('------->')
    
    #输出结果
    ------->
    执行我啦
    
    典型的应用场景:
    
    创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中
    
    当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源
    析构函数,__del__方法
    format_dict={
        'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
        'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
        'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
    }
    class School:
        def __init__(self,name,addr,type):
            self.name=name
            self.addr=addr
            self.type=type
    
        def __repr__(self):
            return 'School(%s,%s)' %(self.name,self.addr)
        def __str__(self):
            return '(%s,%s)' %(self.name,self.addr)
    
        def __format__(self, format_spec):
            # if format_spec
            if not format_spec or format_spec not in format_dict:
                format_spec='nat'
            fmt=format_dict[format_spec]
            return fmt.format(obj=self)
    
    s1=School('oldboy1','北京','私立')
    print('from repr: ',repr(s1))
    print('from str: ',str(s1))
    print(s1)
    
    '''
    str函数或者print函数--->obj.__str__()
    repr或者交互式解释器--->obj.__repr__()
    如果__str__没有被定义,那么就会使用__repr__来代替输出
    注意:这俩方法的返回值必须是字符串,否则抛出异常
    '''
    print(format(s1,'nat'))
    print(format(s1,'tna'))
    print(format(s1,'tan'))
    print(format(s1,'asfdasdffd'))
    自定义打印格式,__str__方法

    单例 (>>传送门点这里<<)

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 2018/04/18 17:47
    # @Author  : MJay_Lee
    # @File    : 单例.py
    # @Contact : limengjiejj@hotmail.com
    
    # 基于元类实现单例模式
    # 单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省空间(场景:假若从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了)
    
    
    
    # 方式一:定义一个类方法实现单例模式
    # import setting
    #
    # class Mysql:
    #     instance = None
    #     def __init__(self,host,port):
    #         self.host = host
    #         self.port = port
    #
    #     @classmethod
    #     def from_conf(self):
    #         if not Mysql.instance:
    #             res = Mysql(setting.HOST, setting.PORT)
    #             Mysql.instance = res
    #         return Mysql.instance
    #
    # # con1 = Mysql('127.0.0.1',80) # <__main__.Mysql object at 0x000000A9F7FC7978>
    # # con2 = Mysql('127.0.0.1',80) # <__main__.Mysql object at 0x000000A9F7FD8710>
    # # con3 = Mysql('127.0.0.1',80) # <__main__.Mysql object at 0x000000A9F7E09C88>
    # # print(con1,con2,con3)
    # #
    #
    # # con1 = Mysql.from_conf() # <__main__.Mysql object at 0x000000BB72BA4DD8>
    # # con2 = Mysql.from_conf() # <__main__.Mysql object at 0x000000BB72BA4E48>
    # # con3 = Mysql.from_conf() # <__main__.Mysql object at 0x000000BB72BA4E80>
    # # print(con1,con2,con3)
    #
    # con1 = Mysql.from_conf() # <__main__.Mysql object at 0x000000BD5BBA4DD8>
    # con2 = Mysql.from_conf() # <__main__.Mysql object at 0x000000BD5BBA4DD8>
    # con3 = Mysql.from_conf() # <__main__.Mysql object at 0x000000BD5BBA4DD8>
    # print(con1 is con2 is con3) # True
    
    
    # # 方式二:定制元类实现
    # # 若从配置文件取相同配置产生对象则实现单例,若传值则新建对象
    import setting
    
    class Mymeta(type):
        def __init__(self,name,bases,dic): # 定义类Mysql时就触发
    
            # 事先从配置文件中取配置来造一个Mysql的实例出来
            self.__instance = object.__new__(self) # 产生对象
            self.__init__(self.__instance,setting.HOST,setting.PORT) # 初始化对象
            #上述两步可合并下面一步
            # self.__instance = super().__call__(*args,**kwargs)
    
            super().__init__(name,bases,dic)
    
        def __call__(self, *args, **kwargs): # Mysql(...)时触发
            if args or kwargs: # Mymeta类的对象括号内传值则新建obj,否则返回self.__instance
                obj = object.__new__(self)
                self.__init__(obj,*args,**kwargs)
                return obj
            return self.__instance
    
    
    # Mysql = Mymeta('Mysql',(obj,),class_dic)
    class Mysql(metaclass=Mymeta):
        def __init__(self,host,port):
            self.host = host
            self.port = port
    
    con1 = Mysql()
    con2 = Mysql()
    # con3 = Mysql() # <__main__.Mysql object at 0x0000008BA7E24DD8>
    # con4 = Mysql('127.0.0.1',80) # <__main__.Mysql object at 0x0000004B4B904EF0>,若Mymeta类的对象(Mysql)括号内传值则新建obj
    # print(con4) # True
    
    
    # 装饰器实现单例
    # import setting
    #
    # def single_obj(cls):
    #     __instance = cls(setting.HOST,setting.PORT)
    #     def wrapper(*args, **kwargs):
    #         if args or kwargs:
    #             obj = cls(*args, **kwargs)
    #             return obj
    #         return __instance
    #     return wrapper
    #
    # @single_obj
    # class Mysql:
    #     def __init__(self,host,port):
    #         self.host = host
    #         self.port = port
    #
    # con1 = Mysql() # <__main__.Mysql object at 0x0000001F978D9C88>
    # con2 = Mysql() # <__main__.Mysql object at 0x0000001F978D9C88>
    # con3 = Mysql('127.0.0.1',80) # <__main__.Mysql object at 0x0000001F98AE4DD8>
    #
    # print(con1 is con2) # True
    三个方法实现单例--示例

    ORM简介

    ORM即Object Relational Mapping,全称对象关系映射
    当我们需要对数据库进行操作时,势必需要通过连接数据、调用sql语句、执行sql语句等操作,ORM将数据库中的表,字段,行与我们面向对象编程的类及其方法,属性等一一对应,即将该部分操作封装起来,程序猿不需懂得sql语句即可完成对数据库的操作。

     

    一、知识储备:

    1、在实例化一个user对象的时候,可以user=User(name='lqz',password='123')

    2 也可以 user=User()

        user['name']='lqz'
        user['password']='123'
    3 也可以 user=User()

        user.name='lqz'
        user.password='password'

    前两种,可以通过继承字典dict来实现,第三种,用getattr和setattr:

    __getattr__ 拦截点号运算。当对未定义的属性名称和实例进行点号运算时,就会用属性名作为字符串调用这个方法。如果继承树可以找到该属性,则不调用此方法

    __setattr__会拦截所有属性的的赋值语句。如果定义了这个方法,self.arrt = value 就会变成self,__setattr__("attr", value).这个需要注意。当在__setattr__方法内对属性进行赋值是,不可使用self.attr = value,因为他会再次调用self,__setattr__("attr", value),则会形成无穷递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.__dict__['name'] = value

     二、定义Model基类

    # 在ModelsMetaclass中自定义拦截实例化对象的方法
    class Models(dict,metaclass=ModelsMetaclass):
        # k,v形式的值
        def __init__(self,**kwargs):
            super().__init__(**kwargs)
    
        # 写存
        def __setattr__(self, key, value):
            self[key] = value
    
        # 读取
        def __getattr__(self, item):
            try:
                return self[item]
            except KeyError:
                raise ('没有该属性')

    三、定义Field

    数据库中每一列数据,都有:列名,列的数据类型,是否是主键,默认值

    # 表示一个列:列名,列的类型,列的主键和默认值
    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):
        def __init__(self,name=None,column_type='varchar(200)',primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)
    
    
    class IntegerField(Field):
        def __init__(self,name=None,column_type='int',primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)

    四、定义元类

    数据库中的每个表,都有表名,每一列的列名,以及主键是哪一列

    既然我要用数据库中的表,对应这一个程序中的类,那么我这个类也应该有这些类属性

    但是不同的类这些类属性又不尽相同,所以我应该怎么做?在元类里拦截类的创建过程,然后把这些东西取出来,放到类里面

    class ModelsMetaclass(type):
        def __new__(cls,name,bases,attrs):
    
            if name == 'Models': #
                return type.__new__(cls, name, bases, attrs)
            table_name = attrs.get('table_name', None) #字典取值,中括号或.get
            if not table_name:
                table_name = name
    
            primary_key = None
            mappings = dict()
            for k, v in attrs.items():
                if isinstance(v, Field):  # v 是不是Field的对象
                    mappings[k] = v
                    if v.primary_key: # v是基类对象,即判断该字段的主键
    
                        # 找到主键
                        if primary_key:
                            raise TypeError('主键重复:%s' % k)
                        primary_key = k
    
            for k in mappings.keys():
                attrs.pop(k) # 执行完此步后,attrs中只剩余有__属性__
            if not primary_key:
                raise TypeError('没有主键')
            attrs['table_name'] = table_name
            attrs['primary_key'] = primary_key
            attrs['mappings'] = mappings
            return type.__new__(cls, name, bases, attrs)

    五、基于pymysql的数据库操作类(单例)

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 2018/05/15 10:44
    # @Author  : MJay_Lee
    # @File    : mysql_singleton.py
    # @Contact : limengjiejj@hotmail.com
    
    import pymysql
    
    class Mysql_interface:
        __instense = None
        def __init__(self):
            self.conn = pymysql.connect(
                host = '127.0.0.1',
                port = 3306,
                user = 'root',
                password = '123456',
                charset = 'utf8',
                database = 'youku',
                autocommit = True
            )
            self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
    
        def close_db(self):
            self.cursor.close()
            self.conn.close()
    
        def select(self,sql,args):
            self.cursor.execute(sql,args)
            re = self.cursor.fetchall()
    
            return re
    
        def execute(self,sql,args):
            try:
                self.cursor.execute(sql,args)
                affected = self.cursor.rowcount
            except BaseException as e:
                print(e)
            return affected
    
        @classmethod
        def singleton(cls):
            if not cls.__instense:
                cls.__instense = cls()
            return cls.__instense
    
    
    
    if __name__ == '__main__':
        ms = Mysql_interface()
        re = ms.select('select * from user where id = %s',1)
        print(re)

    六、继续Models基类

    Models类是所有要对应数据库表类的基类,所以,Models的元类应该是咱们上面写的那个

    而每个数据库表对应类的对象,都应该有查询、插入、保存,方法

    所以:

    # 在ModelsMetaclass中自定义拦截实例化对象的方法
    class Models(dict,metaclass=ModelsMetaclass):
        # k,v形式的值
        def __init__(self,**kwargs):
            super().__init__(**kwargs)
    
        # 写存
        def __setattr__(self, key, value):
            self[key] = value
    
        # 读取
        def __getattr__(self, item):
            try:
                return self[item]
            except KeyError:
                raise ('没有该属性')
    
        @classmethod
        def select_one(cls,**kwargs):
            '''
            查一条
            :param kwargs:
            :return:
            '''
            key = list(kwargs.keys())[0]
            value = kwargs[key]
    
            # select * from user where id=%s
            sql = 'select * from %s where %s =?' % (cls.table_name,key)
            sql = sql.replace('?','%s')
            ms = mysql_singleton.Mysql_interface().singleton()
            re = ms.select(sql,value) # 得到re字典对象
            if re:
                # attrs = {'name':'lmj','password':123}
                # User(**attrs)
                # 相当于 User(name='lmj',password=123)
                return cls(**re[0])
            else:
                return
    
        @classmethod
        def select_many(cls, **kwargs):
            '''
            查多条
            :param kwargs:
            :return:
            '''
            ms = mysql_singleton.Mysql_interface().singleton()
            if kwargs:
                key = list(kwargs.keys())[0]
                value = kwargs[key]
    
                sql = 'select * from %s where %s =?' % (cls.table_name, key)
                sql = sql.replace('?', '%s')
                re = ms.select(sql, value)  # 得到re字典对象
            else:
                sql = 'select * from %s' % (cls.table_name)
                re = ms.select(sql)
            if re:
                obj_list = [cls(**r) for r in re]
                return obj_list
            else:
                return
    
    
        def update(self):
            ms = mysql_singleton.Mysql_interface().singleton()
            # update user set name = ?,password = ? where id = ?
    
            filed_data = [] # name = ?,password = ?
            pr = None
            args = [] # 字段的值
            for k,v in self.mappings.items():
                if v.primary_key:
                    pr = getattr(self,v.name,v.default)
                else:
                    filed_data.append(v.name + '=?')
                    args.append(getattr(self,v.name,v.default))
    
            sql = 'update %s set %s where %s = %s' % (self.table_name,','.join(filed_data),self.primary_key,pr)
            sql = sql.replace('?','%s')
            ms.execute(sql,args)
    
    
        def save(self):
            ms = mysql_singleton.Mysql_interface().singleton()
            # insert into user(name,password) values (?,?)
            field_data = []
            args = []
            value_data = []
            for k,v in self.mappings.items():
                if not v.primary_key:
                    field_data.append(v.name)
                    args.append(getattr(self,v.name,v.default))
                    value_data.append('?')
    
            sql = 'insert into %s(%s) VALUES (%s)' % (self.table_name,','.join(field_data),','.join(value_data))
            sql = sql.replace('?','%s')
            ms.execute(sql,args)

     数据库池版,orm_pool的配置:

    from DBUtils.PooledDB import PooledDB
    import pymysql
    
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
        maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
        maxshared=3,  # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
        blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
        setsession=[],  # 开始会话前执行的命令列表。
        ping=0,
        # ping MySQL服务端,检查是否服务可用。
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123456',
        database='youku',
        charset='utf8',
        autocommit = True
    )
    
    def func():
        # 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常
        # 否则
        # 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。
        # 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。
        # 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。
        # 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。
        conn = POOL.connection()
    
        # print('链接被拿走了', conn._con)
        # print('池子里目前有', POOL._idle_cache, '
    ')
    
        cursor = conn.cursor()
        cursor.execute('select * from user')
        result = cursor.fetchall()
        print(result)
        conn.close()
    
    if __name__ == '__main__':
    
        func()
    orm_pool.py

    mysql_pool的配置:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 2018/05/15 10:44
    # @Author  : MJay_Lee
    # @File    : mysql_pool.py
    # @Contact : limengjiejj@hotmail.com
    
    from video_web_mysql.orm_pool import orm_pool
    import pymysql
    
    class Mysql_interface:
        def __init__(self):
            self.conn = orm_pool.POOL.connection()
            self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
    
        def close_db(self):
            self.cursor.close()
            self.conn.close()
    
        def select(self,sql,args=None):
            self.cursor.execute(sql,args)
            re = self.cursor.fetchall()
    
            return re
    
        def execute(self,sql,args):
            try:
                self.cursor.execute(sql,args)
                affected = self.cursor.rowcount
            except BaseException as e:
                print(e)
            return affected
    
    
    if __name__ == '__main__':
        ms = Mysql_interface()
        re = ms.select('select * from user where id = %s',1)
        print(re)
    mysql_pool.py

    fuckorm的完整源码:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 2018/05/15 8:58
    # @Author  : MJay_Lee
    # @File    : fuckorm.py
    # @Contact : limengjiejj@hotmail.com
    
    from video_web_mysql.orm_pool import mysql_pool
    
    
    # 表示一个列:列名,列的类型,列的主键和默认值
    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):
        def __init__(self,name=None,column_type='varchar(200)',primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)
    
    
    class IntegerField(Field):
        def __init__(self,name=None,column_type='int',primary_key=False,default=None):
            super().__init__(name,column_type,primary_key,default)
    
    
    class ModelsMetaclass(type):
        def __new__(cls,name,bases,attrs):
    
            if name == 'Models': #
                return type.__new__(cls, name, bases, attrs)
            table_name = attrs.get('table_name', None) #字典取值,中括号或.get
            if not table_name:
                table_name = name
    
            primary_key = None
            mappings = dict()
            for k, v in attrs.items():
                if isinstance(v, Field):  # v 是不是Field的对象
                    mappings[k] = v
                    if v.primary_key: # v是基类对象,即判断该字段的主键
    
                        # 找到主键
                        if primary_key:
                            raise TypeError('主键重复:%s' % k)
                        primary_key = k
    
            for k in mappings.keys():
                attrs.pop(k) # 执行完此步后,attrs中只剩余有__属性__
            if not primary_key:
                raise TypeError('没有主键')
            attrs['table_name'] = table_name
            attrs['primary_key'] = primary_key
            attrs['mappings'] = mappings
            return type.__new__(cls, name, bases, attrs)
    
    
    # 在ModelsMetaclass中自定义拦截实例化对象的方法
    class Models(dict,metaclass=ModelsMetaclass):
        # k,v形式的值
        def __init__(self,**kwargs):
            super().__init__(**kwargs)
    
        # 写存
        def __setattr__(self, key, value):
            self[key] = value
    
        # 读取
        def __getattr__(self, item):
            try:
                return self[item]
            except KeyError:
                raise ('没有该属性')
    
        @classmethod
        def select_one(cls,**kwargs):
            '''
            查一条
            :param kwargs:
            :return:
            '''
            key = list(kwargs.keys())[0]
            value = kwargs[key]
    
            # select * from user where id=%s
            sql = 'select * from %s where %s =?' % (cls.table_name,key)
            sql = sql.replace('?','%s')
            ms = mysql_pool.Mysql_interface()
            re = ms.select(sql,value) # 得到re字典对象
            if re:
                # attrs = {'name':'lmj','password':123}
                # User(**attrs)
                # 相当于 User(name='lmj',password=123)
                return cls(**re[0])
            else:
                return
    
        @classmethod
        def select_many(cls, **kwargs):
            '''
            查多条
            :param kwargs:
            :return:
            '''
            ms = mysql_pool.Mysql_interface()
            if kwargs:
                key = list(kwargs.keys())[0]
                value = kwargs[key]
    
                sql = 'select * from %s where %s =?' % (cls.table_name, key)
                sql = sql.replace('?', '%s')
                re = ms.select(sql, value)  # 得到re字典对象
            else:
                sql = 'select * from %s' % (cls.table_name)
                re = ms.select(sql)
            if re:
                obj_list = [cls(**r) for r in re]
                return obj_list
            else:
                return
    
    
        def update(self):
            ms = mysql_pool.Mysql_interface()
            # update user set name = ?,password = ? where id = ?
    
            filed_data = [] # name = ?,password = ?
            pr = None
            args = [] # 字段的值
            for k,v in self.mappings.items():
                if v.primary_key:
                    pr = getattr(self,v.name,v.default)
                else:
                    filed_data.append(v.name + '=?')
                    args.append(getattr(self,v.name,v.default))
    
            sql = 'update %s set %s where %s = %s' % (self.table_name,','.join(filed_data),self.primary_key,pr)
            sql = sql.replace('?','%s')
            ms.execute(sql,args)
    
    
        def save(self):
            ms = mysql_pool.Mysql_interface()
            # insert into user(name,password) values (?,?)
            field_data = []
            args = []
            value_data = []
            for k,v in self.mappings.items():
                # 此处判断是否为自增主键,否则插入时避免还需手动输入主键ID
                if not v.primary_key:
                    field_data.append(v.name)
                    value_data.append('?')
                    args.append(getattr(self, v.name, v.default))
    
            sql = 'insert into %s(%s) VALUES (%s)' % (self.table_name,','.join(field_data),','.join(value_data))
            sql = sql.replace('?','%s')
            ms.execute(sql,args)
    
    
    class User(Models):
        '''
        首先赋值表名
        其次根据数据库表结构来赋值
        '''
        table_name = 'user'
        # k   v(Field的对象)
        id = IntegerField('id',primary_key=True)
        password = StringField('password')
    
    class Notice(Models):
        table_name = 'notice'
        id = IntegerField('id',primary_key=True)
        name = StringField('name')
        content = StringField('content')
        user_id = IntegerField('user_id')
    
    
    if __name__ == '__main__':
        # notice = Notice.select_one(id=1)
        # print(notice.content)
    
        # notice_list = Notice.select_many(id=1)
        # print(notice_list)
    
        # notice.name = '改变了'
        # notice.update()
    
        notice = Notice(name='123',content='新插入',user_id=1)
        notice.save()
    fuckorm.py

    增删改查,基础功能均亲测有效。

  • 相关阅读:
    linux TCP数据包重传过程----小结
    linux TCP头部的构造的简单分析
    CentOS 7镜像下载
    JAVAEE——BOS物流项目03:学习计划、messager、menubutton、登陆拦截器、信息校验和取派员添加功能
    JAVAEE——BOS物流项目02:学习计划、动态添加选项卡、ztree、项目底层代码构建
    JAVAEE——BOS物流项目01:学习计划、搭建环境、主页设计(jQuery EasyUI)
    Maven02——回顾、整合ssh框架、分模块开发、私服
    巧妙取法——最小公倍数
    深度优先搜索——地宫取宝
    Maven01——简介、安装配置、入门程序、项目构建和依赖管理
  • 原文地址:https://www.cnblogs.com/limengjie0104/p/9039127.html
Copyright © 2020-2023  润新知