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"))
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)
只是简单扩展,还可以加入配置文件,标出类属性与数据库字段关系,这样就可以不用字段名与类属性一致,但实现更复杂,目前先做到这个程度,有时间再进一步处理。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)