一、原因

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

  1. 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
  2. 本文链接:https://blog.csdn.net/u013314786/article/details/78226902
  3. ————————————————
  4. import sqlite3
  5.  
  6. class EasySqlite:
  7. """
  8. sqlite数据库操作工具类
  9. database: 数据库文件地址,例如:db/mydb.db
  10. """
  11. _connection = None
  12.  
  13. def __init__(self, database):
  14. # 连接数据库
  15. self._connection = sqlite3.connect(database)
  16.  
  17. def _dict_factory(self, cursor, row):
  18. d = {}
  19. for idx, col in enumerate(cursor.description):
  20. d[col[0]] = row[idx]
  21. return d
  22.  
  23. def execute(self, sql, args=[], result_dict=True, commit=True)->list:
  24. """
  25. 执行数据库操作的通用方法
  26. Args:
  27. sql: sql语句
  28. args: sql参数
  29. result_dict: 操作结果是否用dict格式返回
  30. commit: 是否提交事务
  31. Returns:
  32. list 列表,例如:
  33. [{'id': 1, 'name': '张三'}, {'id': 2, 'name': '李四'}]
  34. """
  35. if result_dict:
  36. self._connection.row_factory = self._dict_factory
  37. else:
  38. self._connection.row_factory = None
  39. # 获取游标
  40. _cursor = self._connection.cursor()
  41. # 执行SQL获取结果
  42. _cursor.execute(sql, args)
  43. if commit:
  44. self._connection.commit()
  45. data = _cursor.fetchall()
  46. _cursor.close()
  47. return data
  48.  
  49. if __name__ == '__main__':
  50. db = EasySqlite('browser.db')
  51. # print(db.execute("select name from sqlite_master where type=?", ['table']))
  52. # print(db.execute("pragma table_info([user])"))
  53. # print(execute("insert into user(id, name, password) values (?, ?, ?)", [2, "李四", "123456"]))
  54. print(db.execute("select id, name userName, password pwd from user"))
  55. print(db.execute("select * from user", result_dict=False))
  56. print(db.execute("select * from user"))
  1. db = EasySqlite('browser.db')
  2. # print(db.execute("select name from sqlite_master where type=?", ['table']))
  3. # print(db.execute("pragma table_info([user])"))
  4. # print(execute("insert into user(id, name, password) values (?, ?, ?)", [2, "李四", "123456"]))
  5. #print(db.execute("select id, name userName, password pwd from user"))
  6. #print(db.execute("select * from user", result_dict=False))
  7. #print(db.execute("select * from user"))

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


二、此工具类的扩展

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

  1. class DbSuper(object):
  2. dbHelper=None #类变量,共用一个EasySqlite工具类
  3.  
  4. def __init__(self):
  5. """
  6. 初始化数据库
  7.  
  8. """
  9. super().__init__()
  10.  
  11. def setDb(self, dburl):
  12. """
  13. 参数:
  14. dburl——数据库文件位置,str类型
  15. """
  16. DbSuper.dbHelper = EasySqlite(dburl)
  17.  
  18. def add(self, obj):
  19. """
  20. 将实例储存到数据库,数据库中的表名应与类名一致,表中字段名与类定义的变量名一致 ,顺序也得一致
  21. 参数:
  22. obj——类实例
  23. 返回值:无
  24. """
  25. sql = 'insert into '+type(obj).__name__+' values(' #通过type(obj).__name__获得表名
  26. paras = [] #sql语句的参数
  27. tag = True
  28. for attr in obj.__dict__.keys(): #获取实例对象的属性名obj.__dict__
  29. if tag:
  30. tag=False #第一项是ID,自动生成,跳过
  31. continue
  32. sql += ',?' #循环几次,就加几次? 生成 insert into xxxx values(,?,?,?,?)的sql语句
  33. para = getattr(obj, attr) # 使用getattr函数,利用反射获得类属性实际的值
  34.  
  35. if type(para)==str: #对值进行判断,如果非str类型,应做转换,避免sql执行错误
  36. paras.append(para)
  37. else :
  38. paras.append(str(para))
  39. sql = sql.replace(',','null,', 1) #将多余的 , 处理一下
  40. sql += ')'
  41. #print(sql)
  42. #print(paras)
  43. DbSuper.dbHelper.execute(sql, paras) #利用工具类执行SQL
  44.  
  45. def findByProperty(self, objclass, propertyName, propertyValueStr,strict = True, orderby='id', pager = False, numPerPage=1, page = 1):
  46. """
  47. 通过类的某一个属性查找
  48. 参数:
  49. objclass——class类型,类名
  50. propertyName——str类型,筛选依据的属性名
  51. propertyValueStr——object类型,筛选依据的属性名对应的值
  52. strict——bool类型,文本字段是否精确匹配,非文本字段请勿改变此值
  53. orderby——str类型,排序的依据,默认ID排序
  54. pager——bool类型,查询的结果是否分页
  55. numPerPage——int类型,如pager=True,则此参数起作用,每页显示数据量
  56. page——int类型,如pager=True,则此参数起作用,页数
  57. 返回值:objclass的list
  58. """
  59. sql = 'select * from %s where ' % objclass.__name__
  60. #对propertyValueStr进行判断,非str型,进行转换
  61. if type(propertyValueStr) != str:
  62. propertyValueStr = str(propertyValueStr)
  63.  
  64. if strict:#默认严格匹配
  65. sql += '%s = ? order by %s '% (propertyName, orderby)
  66. else:
  67. sql += '%s like ? order by %s '% (propertyName, orderby)
  68. propertyValueStr = '%' + propertyValueStr + '%'
  69. if pager: #对pager进行判断,默认不进行分页处理
  70. sql += 'limit %d offset %d' % (numPerPage, numPerPage * (page - 1))
  71. retObjects = []
  72.  
  73. #DbSuper.dbHelper.execute(sql, [propertyValueStr, ])执行SQL,结果返回为Dict数组
  74. print(sql)
  75. for ret in DbSuper.dbHelper.execute(sql, [propertyValueStr, ]):
  76. #利用变量生成实例
  77. obj = objclass()
  78. #调用initByStr方法,将Dict解释,并赋值给对应属性,因不同类实现方式不同,故此方法由类声明时自行完成,类似接口
  79. obj.initByStr(ret)
  80. retObjects.append(obj)
  81. return retObjects
  82.  
  83. def findByPropertyFirst(self, objclass, propertyName, propertyValueStr, strict=True):
  84. """
  85. 类似于findByProperty,做了一定简化,且只查询一个结果
  86. 返回值:成功返回对象实例,失败返回空
  87. """
  88. sql = 'select * from %s where %s = ? limit 1' % (objclass.__name__, propertyName)
  89. if strict==False:
  90. propertyValueStr = '%' + propertyValueStr + '%'
  91. ret = DbSuper.dbHelper.execute(sql, [propertyValueStr, ])
  92. if len(ret)>0:
  93. obj = objclass()
  94. obj.initByStr(ret[0])
  95. return obj
  96. else:
  97. return None
  98.  
  99. def modify(self, obj, propertyIndex='id'):
  100. """
  101. 更新类,并存于数据库
  102. 参数:
  103. obj——类实例
  104. propertyIndex——筛选依据的字段,默认ID
  105. 返回值: 无
  106. """
  107. sql = 'update %s set ' % type(obj).__name__ #利用反射,通过实例获得类名,即表名
  108. params = []
  109. for attr in obj.__dict__.keys(): #遍历每个属性,生成update语句中的set xxx=?,注意要跳过筛选依据的属性
  110. if attr == propertyIndex:
  111. continue
  112. else:
  113. sql += ', %s=?' % attr
  114.  
  115. #对属性值进行处理,如果不是str型,要转换
  116. if type(getattr(obj, attr)) == str:
  117. params.append(getattr(obj, attr))
  118. else:
  119. params.append(str(getattr(obj, attr)))
  120. #筛选条件语句生成
  121. sql += ' where %s = ?' % propertyIndex
  122. #加入参数
  123. params.append(getattr(obj, propertyIndex))
  124. #对生成的sql语句处理,去掉多余的, 执行SQL语句
  125. DbSuper.dbHelper.execute(sql .replace(',', '', 1), params)
  126.  
  127. def delete(self, obj, propertyIndex='id'):
  128. """
  129. 删除对象
  130. 参数:
  131. obj——待删除的对象
  132. propertyIndex——筛选依据
  133.  
  134. """
  135. sql = 'delete from %s where %s=?' % (type(obj).__name__, propertyIndex)
  136. param = getattr(obj, propertyIndex)
  137. if type(param) != str:
  138. param = str(param)
  139. DbSuper.dbHelper.execute(sql , [param, ])

三、使用前提条件

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

四、使用举例

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

  1. CREATE TABLE [operators] (
  2. [id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  3. [loginname] vaRCHAR(20) UNIQUE NOT NULL,
  4. [loginpass] vaRCHAR(100) NOT NULL,
  5. [showname] vaRCHAR(30) NULL,
  6. [level] vaRCHAR(100) NULL
  7. )

2.类operators声明

  1.  

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

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

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

python sqlite3操作类扩展,包含数据库分页的更多相关文章

  1. ACCESS数据库C#操作类(包含事务)

    转自http://blog.csdn.net/allen3010/article/details/6336717 这个是针对ACCESS数据库操作的类,同样也是从SQLHELPER提取而来,分页程序的 ...

  2. Python sqlite3操作笔记

    创建数据库 def create_tables(dbname): conn = sqlite3.connect(dbname) print "Opened database successf ...

  3. APPIUM API整理(python)---操作类

    前言:android手机大家都很熟悉,操作有按键.触摸.点击.滑动等,各种操作方法可以通过api的方法来实现. 参考博文:http://blog.csdn.net/bear_w/article/det ...

  4. 一个数据库操作类,适用于Oracle,ACCESS,SQLSERVER

    最近做了一个数据诊断的项目,里面自己写了一个数据库的操作类,包含:连接数据库.读数据表.执行SQL操作,释放数据库等组成,希望对大家有用,由于水平有限,若有错误或者代码不足地方欢迎指正,谢谢. ADO ...

  5. C#对XML操作类

    C#对XML操作类 该类包含了对XML文件的创建,添加,读取,删除,修改等操作 //#define isUnity #if isUnity using UnityEngine; #endif usin ...

  6. python sqlite3 数据库操作

    python sqlite3 数据库操作 SQLite3是python的内置模块,是一款非常小巧的嵌入式开源数据库软件. 1. 导入Python SQLite数据库模块 import sqlite3 ...

  7. [python][django学习篇][5]选择数据库版本(默认SQLite3) 与操作数据库

    推荐学习博客:http://zmrenwu.com/post/6/ 选择数据库版本(SQLite3) 如果想选择MySQL等版本数据库,请先安装MySQL并且安装python mysql驱动,这里不做 ...

  8. python 数据库操作类

    #安装PyMySQL:pip3 install PyMySQL   #!/usr/bin/python3   #coding=utf-8   #数据库操作类     from  datetime  i ...

  9. PHP 数据库操作类:ezSQL

    EZSQL类介绍: 下载地址:http://www.jb51.net/codes/26393.htmlezsql是一个小型的快速的数据库操作类,可以让你很容易地用PHP操作各种数据库( MySQL.o ...

随机推荐

  1. 手机遥控Office,变身演讲达人

    编者按:在商业演讲中,需要在PPT/Word/Excel文件中切换以达到最佳演讲效果-Office Remote可帮助Windows Phone变身Office的智能遥控.以蓝牙控制电脑,触屏操作多种 ...

  2. MAC上安装maven以及配置Intellij IDEA

    大前提:java环境已经配置好 maven是对于java工程的管理 一.maven安装到mac 1.首先,maven下载地址http://maven.apache.org/download.cgi 点 ...

  3. Mysql存储过程简单应用

    因为很久没写过存储过程了,语法也不记得了,靠百度后,解决了当前问题,这里就简单记录一下. CREATE PROCEDURE pro1() BEGIN DECLARE i int; DECLARE db ...

  4. IoC容器设计

    本文主要摘录于  Spring技术内幕-深入即系Spring架构和设计原理(许文柯著). IoC是根据两个核心BeanFactory和ApplicationContext来设计的,这里先放一张Spri ...

  5. linux kill进程没有立刻停止

    前些天在执行restart脚本的时候遇到了一个奇怪的问题:1.第一次执行进程不见了,启动失败2.第二次重启进程成功,但是在kill的时候提示进程不存在需要重启两次进程才能成功 查看日志文件:第一次重启 ...

  6. Luogu_1080_国王游戏

    题目描述 恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这n位大臣排成一排,国王站在队伍的最前面.排好队 ...

  7. kubernetes集群中的pause容器

    昨天晚上搭建好了k8s多主集群,启动了一个nginx的pod,然而每启动一个pod就伴随这一个pause容器,考虑到之前在做kubelet的systemd unit文件时有见到: 1 2 3 4 5 ...

  8. Centos7.X 搭建Prometheus+node_exporter+Grafana实时监控平台

    Prometheus简介 什么是 Prometheus Prometheus是一个开源监控报警系统和时序列数据库 主要功能 多维数据模型(时序由 metric 名字和 k/v 的 labels 构成) ...

  9. JS实现总价随数量变化而变化(顾客购买商品表单)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:test.html * 作者:常轩 * 微信公众号:Worldh ...

  10. Java入门教程六(内置包装类)

    Java 是一种面向对象的编程语言,Java 中的类把方法与数据类型连接在一起,构成了自包含式的处理单元.但在 Java 中不能定义基本类型对象,为了能将基本类型视为对象处理,并能连接相关方法,Jav ...