zoukankan      html  css  js  c++  java
  • python sqlite3操作类扩展,包含数据库分页

     一、原因

    最近在使用python3和sqlite3编辑一些小程序,由于要使用数据库,就离不开增、删、改、查,sqlite3的操作同java里的jdbc很像,于是就想找现成的操作类,找来找去,发现一个相对来说简单的封装,代码如下:

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/u013314786/article/details/78226902
    ————————————————
    import sqlite3
     
     
    class EasySqlite:
        """
        sqlite数据库操作工具类
        database: 数据库文件地址,例如:db/mydb.db
        """
        _connection = None
     
        def __init__(self, database):
            # 连接数据库
            self._connection = sqlite3.connect(database)
     
        def _dict_factory(self, cursor, row):
            d = {}
            for idx, col in enumerate(cursor.description):
                d[col[0]] = row[idx]
            return d
     
        def execute(self, sql, args=[], result_dict=True, commit=True)->list:
            """
            执行数据库操作的通用方法
            Args:
            sql: sql语句
            args: sql参数
            result_dict: 操作结果是否用dict格式返回
            commit: 是否提交事务
            Returns:
            list 列表,例如:
            [{'id': 1, 'name': '张三'}, {'id': 2, 'name': '李四'}]
            """
            if result_dict:
                self._connection.row_factory = self._dict_factory
            else:
                self._connection.row_factory = None
            # 获取游标
            _cursor = self._connection.cursor()
            # 执行SQL获取结果
            _cursor.execute(sql, args)
            if commit:
                self._connection.commit()
            data = _cursor.fetchall()
            _cursor.close()
            return data
     
     
    if __name__ == '__main__':
        db = EasySqlite('browser.db')
        # print(db.execute("select name from sqlite_master where type=?", ['table']))
        # print(db.execute("pragma table_info([user])"))
        # print(execute("insert into user(id, name, password) values (?, ?, ?)", [2, "李四", "123456"]))
        print(db.execute("select id, name userName, password pwd from user"))
        print(db.execute("select * from user", result_dict=False))
        print(db.execute("select * from user"))
    View Code
        db = EasySqlite('browser.db')
        # print(db.execute("select name from sqlite_master where type=?", ['table']))
        # print(db.execute("pragma table_info([user])"))
        # print(execute("insert into user(id, name, password) values (?, ?, ?)", [2, "李四", "123456"]))
        #print(db.execute("select id, name userName, password pwd from user"))
        #print(db.execute("select * from user", result_dict=False))
        #print(db.execute("select * from user"))

    执行的方式如上一段代码,大体上是初始化时传入sqlite3数据库路径,使用db.excecute方法来执行sql,返回的是Dict数组。


    二、此工具类的扩展

    但一个类写相同的增、删、改、查,感觉很费时间,于是想借鉴java的反射机制,尝试使用python的反射来实现MVC中的module基类,得到以下代码:

    class DbSuper(object):
        dbHelper=None   #类变量,共用一个EasySqlite工具类
        
        def __init__(self):
            """
                初始化数据库
                
            """
            super().__init__()
            
        def setDb(self,  dburl): 
            """
                参数:
                    dburl——数据库文件位置,str类型
            """
            DbSuper.dbHelper = EasySqlite(dburl)   
            
        
        def add(self, obj):
            """
                将实例储存到数据库,数据库中的表名应与类名一致,表中字段名与类定义的变量名一致 ,顺序也得一致
                参数:
                    obj——类实例
                返回值:无    
            """
            sql = 'insert into '+type(obj).__name__+' values('  #通过type(obj).__name__获得表名
            paras = []  #sql语句的参数
            tag = True
            for attr in obj.__dict__.keys():   #获取实例对象的属性名obj.__dict__
                if tag:
                    tag=False  #第一项是ID,自动生成,跳过
                    continue
                sql += ',?'   #循环几次,就加几次? 生成   insert into xxxx values(,?,?,?,?)的sql语句
                para = getattr(obj, attr)    # 使用getattr函数,利用反射获得类属性实际的值
                
                if type(para)==str:    #对值进行判断,如果非str类型,应做转换,避免sql执行错误
                    paras.append(para)
                else :
                    paras.append(str(para))
            sql = sql.replace(',','null,', 1)    #将多余的 , 处理一下
            sql += ')'
            #print(sql)
            #print(paras)
            DbSuper.dbHelper.execute(sql, paras)   #利用工具类执行SQL
            
        def findByProperty(self, objclass, propertyName,  propertyValueStr,strict = True,  orderby='id', pager = False, numPerPage=1, page = 1):
            """
                通过类的某一个属性查找
                参数:
                    objclass——class类型,类名
                    propertyName——str类型,筛选依据的属性名
                    propertyValueStr——object类型,筛选依据的属性名对应的值
                    strict——bool类型,文本字段是否精确匹配,非文本字段请勿改变此值
                    orderby——str类型,排序的依据,默认ID排序
                    pager——bool类型,查询的结果是否分页
                    numPerPage——int类型,如pager=True,则此参数起作用,每页显示数据量
                    page——int类型,如pager=True,则此参数起作用,页数
                返回值:objclass的list
            """
            sql = 'select * from %s where ' % objclass.__name__
            #对propertyValueStr进行判断,非str型,进行转换
            if type(propertyValueStr) != str:
                propertyValueStr = str(propertyValueStr)
                
            if strict:#默认严格匹配
                sql += '%s = ? order by %s '% (propertyName, orderby)
            else:
                sql += '%s like ? order by %s '% (propertyName, orderby)
                propertyValueStr = '%' + propertyValueStr + '%'
            if pager: #对pager进行判断,默认不进行分页处理
                sql += 'limit %d offset %d' % (numPerPage, numPerPage * (page - 1))
            retObjects = []
            
                #DbSuper.dbHelper.execute(sql, [propertyValueStr, ])执行SQL,结果返回为Dict数组
            print(sql)
            for ret in  DbSuper.dbHelper.execute(sql, [propertyValueStr, ]):
                #利用变量生成实例
                obj = objclass()
                #调用initByStr方法,将Dict解释,并赋值给对应属性,因不同类实现方式不同,故此方法由类声明时自行完成,类似接口
                obj.initByStr(ret)
                retObjects.append(obj)
            return retObjects
            
        def findByPropertyFirst(self, objclass, propertyName,  propertyValueStr, strict=True):
            """
                类似于findByProperty,做了一定简化,且只查询一个结果
                返回值:成功返回对象实例,失败返回空
            """
            sql = 'select * from %s where %s = ? limit 1' % (objclass.__name__, propertyName)
            if strict==False:
                propertyValueStr = '%' + propertyValueStr + '%'
            ret = DbSuper.dbHelper.execute(sql,  [propertyValueStr, ])
            if len(ret)>0:
                obj = objclass()
                obj.initByStr(ret[0])
                return obj 
            else:
                return None
                
        def modify(self, obj, propertyIndex='id'):
            """
                更新类,并存于数据库
                参数:
                    obj——类实例
                    propertyIndex——筛选依据的字段,默认ID
                返回值: 无
            """
            sql = 'update %s set ' % type(obj).__name__     #利用反射,通过实例获得类名,即表名
            params = []
            for attr in obj.__dict__.keys():        #遍历每个属性,生成update语句中的set xxx=?,注意要跳过筛选依据的属性
                if attr == propertyIndex:
                    continue
                else:
                    sql += ', %s=?'  % attr 
                    
                    #对属性值进行处理,如果不是str型,要转换
                    if type(getattr(obj, attr)) == str:
                        params.append(getattr(obj, attr))
                    else:
                        params.append(str(getattr(obj, attr)))
            #筛选条件语句生成
            sql += ' where %s = ?' % propertyIndex
            #加入参数
            params.append(getattr(obj, propertyIndex))
            #对生成的sql语句处理,去掉多余的,   执行SQL语句
            DbSuper.dbHelper.execute(sql .replace(',', '', 1), params)
        
        def delete(self, obj, propertyIndex='id'):
            """
                删除对象
                参数:
                    obj——待删除的对象
                    propertyIndex——筛选依据
                    
            """
            sql = 'delete from %s where %s=?' % (type(obj).__name__, propertyIndex)
            param = getattr(obj, propertyIndex)
            if type(param) != str:
                param = str(param)
            DbSuper.dbHelper.execute(sql , [param, ])

     三、使用前提条件

    • 类名要与数据库中表名一致
    • 类中属性与数据库中字段名一致
    • 为解决查询结果转换成类的问题,类中要实现一个方法initByStr

    四、使用举例

    1.数据库中表创建示例,注意表名operators,此处模拟一用户基本信息

    CREATE TABLE [operators] (
    [id] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,
    [loginname] vaRCHAR(20)  UNIQUE NOT NULL,
    [loginpass] vaRCHAR(100)  NOT NULL,
    [showname] vaRCHAR(30)  NULL,
    [level] vaRCHAR(100)  NULL
    )

    2.类operators声明

    
    

    # -*- coding=utf-8 -*-
    from enum import Enum
    import abc


    class
    Levels(Enum): """ 枚举类,标明权限类型 """ DATA_INPUTER='查询数据,录入数据,修改数据' USER_MANAGER='增加用户,修改用户基本信息' POWER_MANAGER='增加用户,修改用户基本信息,修改用户权限' class DbEntity(object): @abc.abstractmethod def initByStr(self, attrDict): pass class Operators(DbEntity): """ 用户类 """ def __init__(self): super().__init__() self.id=0 self.loginName='' self.loginPass='' self.showName='' self.level=Levels.DATA_INPUTER def initByStr(self, attrDict): if len(attrDict)==5: self.id = int(attrDict['id']) self.loginName = attrDict['loginname'] self.loginPass = attrDict['loginpass'] self.showName = attrDict['showname'] self.level = Levels(attrDict['level'])
    DbEntity是基类,只声明了一个接口initByStr,子类必须实现,原本我想在扩展类里实现这个方法,但也只能实现基本数据类型,一旦类里的属性比较复杂也不好实现,所以还是由类中声明每一个字符串如何转化成类。
    3.准备工作完成后,下面实现OperatorDao,代码如下:
    class OperatorDao(DbSuper):
        
        def __init__(self):
            super().__init__()
        
        def findById(self, id):
            """
                根据ID查找类
                返回类,如未找到返回空
            """
            return super().findByPropertyFirst(Operators, 'id', id)
                
    
        def findByLoginname(self, loginname):
            """
                根据登录名查找类
                返回类,如未找到返回空
            """
            return super().findByPropertyFirst(Operators, 'loginName', loginname)
            #return super().findByProperty(Operators, 'loginName', loginname)
            #return super().findByProperty(Operators, 'loginName', loginname,strict=False)
            #return super().findByProperty(Operators, 'loginName', loginname, pager = True, numPerPage=5, page = 1)
        
        def addOper(self, oper):
            #可以对实例进一步处理,比如MD5加密 oper.loginPass = MDUtils.md5Text(oper.loginPass)
            return super().add(oper)
        
        def modiOper(self, oper):
            return super().modify(oper)
        
        def delOper(self, oper):
            return super().delete(oper)
    
    
        
    if __name__ == '__main__':
        operatorDao = OperatorDao()
        operatorDao.setDb('xxxxxx.s3db')
        oper = operatorDao.findByLoginname('test')
        for op in oper:
            print(op)

    只是简单扩展,还可以加入配置文件,标出类属性与数据库字段关系,这样就可以不用字段名与类属性一致,但实现更复杂,目前先做到这个程度,有时间再进一步处理。

  • 相关阅读:
    李开复给学习计算机的学生的7点建议(转)
    linux 命令
    易中天的十句话
    11个笑话让你领悟人生
    心情不好的时候看看
    高校青年老师挣扎在辞职边缘 微薄工资继续啃老
    【33.00%】【vijos P1002】过河
    【33.33%】【codeforces 681D】Gifts by the List
    【19.05%】【codeforces 680D】Bear and Tower of Cubes
    【12.78%】【codeforces 677D】Vanya and Treasure
  • 原文地址:https://www.cnblogs.com/aocshallo1/p/11689730.html
Copyright © 2011-2022 走看看