Python 手写ORM-我的一个数据库访问工具(三)

第一节整体介绍了ef-python,第二节把整体集合类Collection完成了,而本节我们来完成ORM的核心,对象映射
第一节:ORM的使用Python 手写ORM-我的一个数据库访问工具(一)
第二节:Collection类的构造Python 手写ORM-我的一个数据库访问工具(二)

文章目录

  • 对DBCollection的小修饰
  • DBRecord对象
    • 内部函数
  • DBSet对象
    • __init__函数
    • flush函数
    • savechanges函数
    • where函数
    • find函数
    • add函数
  • DBContext对象

对DBCollection的小修饰

因为对于ORM对象,其添加对象会更加严谨,所以要禁用__iadd__add函数

DBRecord对象

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 可追踪的Record

objcmp

参数名称 类型 说明
object1 TableObject 比对对象1
object2 TableObject 比对对象2

返回值
bool 是否相等

DBSet对象

__init__函数

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用于储存映射表对象

该函数可以通过表名获取主键和字段来初始化,并且自动构造映射表对象然后载入对象

flush函数

    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)

清空记录并且刷新查询

savechanges函数

    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()

保存修改并刷新,通过对追踪对象比较现在的值和之前的值是否相同,不相同则更新,相同则跳过

where函数

    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 筛选规则

find函数

    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 查询的主键值

add函数

    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 增加的对象

DBContext对象

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对象,编写自己的上下文对象是需继承该对象

点击下载全文代码

你可能感兴趣的:(学习日志,python,数据库,开发语言)