第一节整体介绍了ef-python
,第二节把整体集合类Collection
完成了,而本节我们来完成ORM的核心,对象映射
第一节:ORM的使用Python 手写ORM-我的一个数据库访问工具(一)
第二节:Collection类的构造Python 手写ORM-我的一个数据库访问工具(二)
因为对于ORM对象,其添加对象会更加严谨,所以要禁用__iadd__
和add
函数
class Record:
def __init__(self, data, mainkey, sign=None):
self.data = data
self.sign = sign
self.mainkey = mainkey
对于维护的数据,为了更好的查找会使用DBRecord对象去储存
该文件中的内部函数
def findkey(source, mainkey):
for i in source:
if i.mainkey == mainkey:
return i
def findsign(source):
data = []
for i in source:
if i.sign is not None:
data.append(i)
return data
def objcmp(object1, object2):
for i, j in zip(object1.__dict__, object2.__dict__):
if object1.__dict__[i] != object2.__dict__[j]:
return False
return True
findkey
函数
参数名称 | 类型 | 说明 |
---|---|---|
source | list |
待Record集合 |
mainkey | string | 主键 |
返回值
索引 int
findsign
参数名称 | 类型 | 说明 |
---|---|---|
source | Record | 待查找的Record |
返回值
list
objcmp
参数名称 | 类型 | 说明 |
---|---|---|
object1 | TableObject | 比对对象1 |
object2 | TableObject | 比对对象2 |
返回值
bool 是否相等
DBSet对象是ORM中数据表的映射
def __init__(self, context, table, init=None):
self._table = table
if init is None:
init = context.create()
self._cursor = init[1]
self._conn = init[0]
self._view = Collection()
self._mainkey = None
self._lines = []
# 查找所有字段名,生成
sqlstring = f'select COLUMN_NAME from information_schema.columns where table_name=\'{self._table}\''
self._cursor.execute(sqlstring)
result = list(self._cursor.fetchall())
self._keys = []
for key in result:
self._keys.append(key[0])
self._model = type(self._table, (object,), {})
sqlstring = f'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME=\'{self._table}\''
self._cursor.execute(sqlstring)
self._mainkey = self._cursor.fetchall()[0][0]
sqlstring = f'Select * from {self._table}'
self._cursor.execute(sqlstring)
result = self._cursor.fetchall()
# 转换数据类型
for datas in result:
obj = self._model()
for data, key in zip(datas, self._keys):
obj.__dict__[key] = data
record = Record(obj, obj.__dict__[self._mainkey])
self._view.add(record)
参数名 | 类型 | 说明 |
---|---|---|
context | DBContext | 创建的DBContext |
table | string | Table名称 |
代码解读
self._table = table
self._cursor = init[1]
self._conn = init[0]
self._view = Collection()
self._mainkey = None
self._lines = []
self._keys = []
self._model = type(self._table, (object,), {})
self._table用于储存表名,self._cursor和self._conn用于储存游标对象
和连接对象
,而DBContext有一个内部方法.create()
函数用于返回这两个对象进行初始化,self._view用于储存查询对象,self._mainkey储存主键,self._lines用于储存过程语句
,self._key用于储存所有的字段,self._model用于储存映射表对象
该函数可以通过表名获取主键和字段来初始化,并且自动构造
映射表对象然后载入对象
def flush(self):
self._lines.clear()
self._view.clear()
sqlstring = f'Select * from {self._table}'
self._cursor.execute(sqlstring)
result = self._cursor.fetchall()
# 转换数据类型
for datas in result:
obj = self._model()
for data, key in zip(datas, self._keys):
obj.__dict__[key] = data
record = Record(obj, obj.__dict__[self._mainkey])
self._view.add(record)
清空记录并且刷新查询
def savechanges(self):
# 判断移出对象是否改变
changes = findsign(self._view)
for i in changes:
# 修改所有的值
if objcmp(i.data, i.sign):
continue
else:
# 上传修改了的值
data = ''
for key, value in zip(i.data.__dict__.keys(), i.data.__dict__.values()):
data += f'{key}=' + f'\'{value}\'' + ','
data = str(data).removesuffix(',')
sqlstring = f'update {self._table} set {data} where {self._mainkey}=\'{i.sign.__dict__[self._mainkey]}\''
self._lines.append(sqlstring)
# 上传每一条修改语句
for i in self._lines:
print(i)
self._cursor.execute(i)
# 清除所有执行语句
self.flush()
self._conn.commit()
保存修改并刷新,通过对追踪对象比较现在的值和之前的值是否相同,不相同则更新,相同则跳过
def where(self, expression):
data = Collection()
for item in self._view:
if expression(item.data):
item.sign = copy.copy(item.data)
data.add(item.data)
return data
参数名 | 类型 | 说明 |
---|---|---|
expression | function | 筛选规则 |
def find(self, key):
result = self._view.where(lambda x: x.data.__dict__[self._mainkey] == key).firstordefault()
if result is None:
return None
result.sign = copy.copy(result.data)
return result.data
参数名 | 类型 | 说明 |
---|---|---|
key | string | 查询的主键值 |
def add(self, item):
if type(item) != self._model:
raise TypeError()
else:
self._view.add(Record(item, item.__dict__[self._mainkey]))
data = str(tuple(item.__dict__.values())).replace('None', 'Null')
sqlstring = f'insert into {self._table} values {data}'
self._lines.append(sqlstring)
参数名 | 类型 | 说明 |
---|---|---|
item | TableObject | 增加的对象 |
class DBContext(object):
def __init__(self, con):
self._conn = con
self._cursor = self._conn.cursor()
def create(self):
return self._conn, self._cursor
def add(self, item):
# 自分配增加对象
for dbset in self.__dict__.values():
if type(dbset) is DBSet:
if dbset.__dict__['_model'] is type(item):
dbset.add(item)
return
raise Exception('Error Could Not Find DBSet')
def savechanges(self):
for dbset in self.__dict__.values():
if type(dbset) is DBSet:
dbset.savechanges()
def execute(self, sqlstring):
self._cursor.execute(sqlstring)
result = self._cursor.fetchall()
self._conn.commit()
return result
def close(self):
self._cursor.close()
self._conn.close()
del self
基DBContext对象,编写自己的上下文对象是需继承该对象
点击下载全文代码