如何使用Python实现一个简易的ORM模型


Posted in Python onMay 12, 2021

本文记录下自己使用Python实现一个简易的ORM模型

使用到的知识

1、元类

2、描述器

元类

对于元类,我的理解其实也便较浅,大概是这个意思

所有的类都是使用元类来进行创建的,而所有的类的父类中必然是object(针对Python3),Python中的元类只有一个(type),当然这里不包含自定义元类

下面我们来看下类的创建

class Test:   # 定义一个类
    pass

Test1 = type("Test2",(object,),{"name":"test"})  # 定义一个类


print(type(Test))
print(type(Test1))-----------------------
<class 'type'><class 'type'>

从上面可以看出创建类,其实是有两种方式,一种是通过class关键字来定义,一种是通过type来进行创建,当然常用的是使用class来进行创建了,在看最后的结果,可以看出类的类型为type。说明我们这个类就是由type来创建的

明白了这个之后我们再来梳理下怎么使用自定义元类来创建类,明白一点,自定义元类需要继承type

class MetaClass(type):  # 定义一个元类
    pass


class Test(metaclass=MetaClass):  # 使用自定义元类来创建类
    pass



print(type(Test))


--------------------------

<class '__main__.MetaClass'>

很明显可以看出Test类就是用MetaClass类创建出来的

描述器

从描述器的定义来说,只要一个类中实现了__get__、__set__、__delete__中的一个或几个,这个类的实例就可以叫描述器

下面我们来定义一个简易的描述器

class Describer:

    def __set__(self, instance, value):
        print("设置属性的时候会被调用")
        self.value = value

    def __get__(self, instance, owner):
        print("获取属性的时候会被调用")
        return self.value

    def __delete__(self, instance):
        print("删除属性的时候会被调用")
        self.value = None


class Test:
    name = Describer()



t = Test()
t.name = "xxxxx"
print(t.name)

----------------------

设置属性的时候会被调用
获取属性的时候会被调用
xxxxx

从上面的代码中有没有什么想法?既然__set__方法会在我们设置属性的时候会被调用,那么我们是不是可以在设置属性前对这个属性做一些操作呢?

ORM模型

ORM模型到底是个啥?ORM对于后端研发来说肯定是不陌生的,包括很多后端框架现在都自带这个模型了

ORM(Object Relational Mapping)对象关系映射

既然是对象关系映射,那对象是啥?我的理解为:Python中的类与数据库之间的映射,对数据的操作就不用编写SQL语言了,因为都封装好了,比如你想插入一条数据,你就直接创建一个对象即可,

Python ------->>>>      数据库

类名     ------->>>>      数据库中的表名

对象     ------->>>>      数据库中的一行数据

属性     ------->>>>      数据库中的字段

大致就是上面的映射关系

ORM实现步骤

1、利用描述器实现对数据库字段的类型、长度限制

2、实现Mode类,也就是Python中的类

3、利用元类实现映射关系

好,我们先利用描述器来实现对数据字段的类型,长度限制

class BaseFiled:
    pass


class CharFiled(BaseFiled):
    """定义一个字符串的类型限制"""

    def __init__(self, length=10):
        self.length = length

    def __set__(self, instance, value):
        if isinstance(value, str):
            if len(value) <= self.length:
                self.value = value
            else:
                raise ValueError("length can not exceed {}".format(self.length))
        else:
            raise TypeError("need a str")

    def __get__(self, instance, owner):
        return self.value

    def __delete__(self, instance):
        self.value = None


class IntFiled(BaseFiled):
    """定义一个数值的类型限制"""

    def __set__(self, instance, value):
        if isinstance(value, int):

            self.value = value
        else:
            raise TypeError("need a int")

    def __get__(self, instance, owner):
        return self.value

    def __delete__(self, instance):
        self.value = None


class BoolFiled(BaseFiled):
    """定义一个布尔的类型限制"""
    def __set__(self, instance, value):
        if isinstance(value, bool):

            self.value = value
        else:
            raise TypeError("need a bool")

    def __get__(self, instance, owner):
        return self.value

    def __delete__(self, instance):
        self.value = None

上面实现了三种,分别是字符串、数值、布尔值的,下面在来实现元类以及模型类

class MyMateClass(type):
    """自定义一个元类"""
    def __new__(cls, name: str, bases: tuple, dic: dict, *args, **kwargs):
        """

        :param name: name为模型类的类名也就是数据库中的表名
        :param bases:  bases为一个元祖类型,里面装的是name这个类的父类
        :param dic: dic为一个dict类型,装的是name这个类中的属性
        :param args:
        :param kwargs:
        :return:
        """
        if name == "BaseMode":   # 判断类名是否为BaseMode,如果是则直接使用元类创建类,不做其他任何操作
            return super().__new__(cls, name, bases, dic)
        else:
            table_name = name.lower()  # 将表名变成小写
            filed_dic = {}   # 定义一个空的列表,用来装dic中属于BaseFiled类型的属性,因为dic中会有其他创建类时自动生成的属性,这些属性我们没必要去建立映射关系,所以需要将其剔除掉
            for k, v in dic.items():
                if isinstance(v, BaseFiled):
                    filed_dic[k] = v
            dic["t_name"] = table_name    # 将表名添加到dic中,实现类名与表名的映射关系
            dic["filed_dict"] = filed_dic  # 将属于BaseFiled类型的属性给添加到dic中,实现属性与字段的映射关系
            return super().__new__(cls, name, bases, dic)


class BaseMode(metaclass=MyMateClass):

    def __init__(self, **kwargs):
        """
        由于每一个模型类(也就是数据库表)的属性个数不一致,所以我们需要定义一个父类来进行定义初始化的属性
        :param kwargs:
        """
        for k, v in kwargs.items():   # 遍历传进来的所有属性
            setattr(self, k, v)   # 拿到这些属性后对self(也就是类本身)进行设置属性

    def save(self):
        """生成SQL语句"""
        # 获取表名
        table_name = self.t_name
        # 获取所有的属性
        fileds = self.filed_dict
        dic = {}  # 定义一个空字典,用来装属性名和属性值
        for k, v in fileds.items():
            value = getattr(self, k)
            dic[k] = value
        sql = "insert into {} values{}".format(table_name, tuple(dic.values()))
        return sql


class User(BaseMode):
    name = CharFiled()
    age = IntFiled()
    love = CharFiled(length=50)
    live = BoolFiled()


if __name__ == '__main__':
    c = User(name="lc", age=12, love="hjh", live=True)
    c.save()




--------------------------
insert into user values('lc', 12, 'hjh', True)

以上就实现了一个简单的ORM模型了,这个虽然在测试开发过程中用的很少(一般都是直接用框架中封装好的),学习这个也是为了更好的理解原理,后面好学习flask以及Django。

下面贴一下完整的代码吧

# -*- coding: utf-8 -*-
# @Time    : 2021-05-11 10:14
# @Author  : cainiao
# @File    : Meat.py
# @Software: PyCharm
# @Content : 实现ORM模型

class BaseFiled:
    pass


class CharFiled(BaseFiled):
    """定义一个字符串的类型限制"""

    def __init__(self, length=10):
        self.length = length

    def __set__(self, instance, value):
        if isinstance(value, str):
            if len(value) <= self.length:
                self.value = value
            else:
                raise ValueError("length can not exceed {}".format(self.length))
        else:
            raise TypeError("need a str")

    def __get__(self, instance, owner):
        return self.value

    def __delete__(self, instance):
        self.value = None


class IntFiled(BaseFiled):
    """定义一个数值的类型限制"""

    def __set__(self, instance, value):
        if isinstance(value, int):

            self.value = value
        else:
            raise TypeError("need a int")

    def __get__(self, instance, owner):
        return self.value

    def __delete__(self, instance):
        self.value = None


class BoolFiled(BaseFiled):
    """定义一个数值的类型限制"""

    def __set__(self, instance, value):
        if isinstance(value, bool):

            self.value = value
        else:
            raise TypeError("need a bool")

    def __get__(self, instance, owner):
        return self.value

    def __delete__(self, instance):
        self.value = None


class MyMateClass(type):
    """自定义一个元类"""
    def __new__(cls, name: str, bases: tuple, dic: dict, *args, **kwargs):
        """

        :param name: name为模型类的类名也就是数据库中的表名
        :param bases:  bases为一个元祖类型,里面装的是name这个类的父类
        :param dic: dic为一个dict类型,装的是name这个类中的属性
        :param args:
        :param kwargs:
        :return:
        """
        if name == "BaseMode":   # 判断类名是否为BaseMode,如果是则直接使用元类创建类,不做其他任何操作
            return super().__new__(cls, name, bases, dic)
        else:
            table_name = name.lower()  # 将表名变成小写
            filed_dic = {}   # 定义一个空的列表,用来装dic中属于BaseFiled类型的属性,因为dic中会有其他创建类时自动生成的属性,这些属性我们没必要去建立映射关系,所以需要将其剔除掉
            for k, v in dic.items():
                if isinstance(v, BaseFiled):
                    filed_dic[k] = v
            dic["t_name"] = table_name    # 将表名添加到dic中,实现类名与表名的映射关系
            dic["filed_dict"] = filed_dic  # 将属于BaseFiled类型的属性给添加到dic中,实现属性与字段的映射关系
            return super().__new__(cls, name, bases, dic)


class BaseMode(metaclass=MyMateClass):

    def __init__(self, **kwargs):
        """
        由于每一个模型类(也就是数据库表)的属性个数不一致,所以我们需要定义一个父类来进行定义初始化的属性
        :param kwargs:
        """
        for k, v in kwargs.items():   # 遍历传进来的所有属性
            setattr(self, k, v)   # 拿到这些属性后对self(也就是类本身)进行设置属性

    def save(self):
        """生成SQL语句"""
        # 获取表名
        table_name = self.t_name
        # 获取所有的属性
        fileds = self.filed_dict
        dic = {}  # 定义一个空字典,用来装属性名和属性值
        for k, v in fileds.items():
            value = getattr(self, k)
            dic[k] = value
        sql = "insert into {} values{}".format(table_name, tuple(dic.values()))
        return sql


class User(BaseMode):
    name = CharFiled()
    age = IntFiled()
    love = CharFiled(length=50)
    live = BoolFiled()


if __name__ == '__main__':
    c = User(name="lc", age=12, love="hjh", live=True)
    print(c.save())
    # c.name="lc"
    # print(c.name)

以上就是如何使用Python实现一个简易的ORM模型的详细内容,更多关于python 实现ORM模型的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
全面了解Python环境配置及项目建立
Jun 30 Python
Python实现的多线程同步与互斥锁功能示例
Nov 30 Python
PyCharm的设置方法和第一个Python程序的建立
Jan 16 Python
python使用phoenixdb操作hbase的方法示例
Feb 28 Python
python登录WeChat 实现自动回复实例详解
May 28 Python
使用Pyinstaller转换.py文件为.exe可执行程序过程详解
Aug 06 Python
django实现后台显示媒体文件
Apr 07 Python
jupyter note 实现将数据保存为word
Apr 14 Python
python mysql自增字段AUTO_INCREMENT值的修改方式
May 18 Python
如何基于Python代码实现高精度免费OCR工具
Jun 18 Python
Python如何读写字节数据
Aug 05 Python
ubuntu安装jupyter并设置远程访问的实现
Mar 31 Python
用python删除文件夹中的重复图片(图片去重)
May 12 #Python
Pyhton模块和包相关知识总结
python 下划线的多种应用场景总结
May 12 #Python
超级详细实用的pycharm常用快捷键
pycharm 如何查看某一函数源码的快捷键
教你使用Pandas直接核算Excel中快递费用
用python开发一款操作MySQL的小工具
May 12 #Python
You might like
PHP表单提交表单名称含有点号(.)则会被转化为下划线(_)
2011/12/14 PHP
php导入csv文件碰到乱码问题的解决方法
2014/02/10 PHP
无需数据库在线投票调查php代码
2016/07/20 PHP
php实现微信公众号创建自定义菜单功能的实例代码
2019/06/11 PHP
Thinkphp集成抖音SDK的实现方法
2020/04/28 PHP
基于Jquery的实现回车键Enter切换焦点
2010/09/14 Javascript
关于scrollLeft,scrollTop的浏览器兼容性测试
2013/03/19 Javascript
gridpanel动态加载数据的实例代码
2013/07/18 Javascript
JS判断对象是否存在的10种方法总结
2013/12/23 Javascript
Web打印解决方案之证件套打的实现思路
2016/08/29 Javascript
jQuery给指定的table动态添加删除行的操作方法
2016/10/12 Javascript
AngularJS 中使用Swiper制作滚动图不能滑动的解决方法
2016/11/15 Javascript
jQuery基于ajax操作json数据简单示例
2017/01/05 Javascript
bootstrap模态框示例代码分享
2017/05/17 Javascript
基于node搭建服务器,写接口,调接口,跨域的实例
2018/05/13 Javascript
微信小程序实现左右列表联动
2020/05/19 Javascript
JavaScript日期库date-fn.js使用方法解析
2020/09/09 Javascript
CentOS安装pillow报错的解决方法
2016/01/27 Python
Python正则表达式匹配中文用法示例
2017/01/17 Python
使用Python爬了4400条淘宝商品数据,竟发现了这些“潜规则”
2018/03/23 Python
ubuntu17.4下为python和python3装上pip的方法
2018/06/12 Python
学习Python列表的基础知识汇总
2020/03/10 Python
利用Python裁切tiff图像且读取tiff,shp文件的实例
2020/03/10 Python
Anaconda+Pycharm环境下的PyTorch配置方法
2020/03/13 Python
python使用建议与技巧分享(二)
2020/08/17 Python
详解使用Python写一个向数据库填充数据的小工具(推荐)
2020/09/11 Python
Anaconda详细安装步骤图文教程
2020/11/12 Python
Debenhams百货英国官方网站:Debenhams UK
2016/07/12 全球购物
顶级宝石首饰网络零售商:Angara
2016/10/25 全球购物
Boda Skins皮衣官网:奢侈皮夹克,全球配送
2016/12/15 全球购物
Jowissa官方网站:瑞士制造的手表,优雅简约的设计
2020/07/29 全球购物
安全教育主题班会教案
2015/08/12 职场文书
小学生班干部竞选稿
2015/11/20 职场文书
考教师资格证不要错过的4个最佳时机
2019/07/17 职场文书
利用html+css实现菜单栏缓慢下拉效果的示例代码
2021/03/30 HTML / CSS
python自然语言处理之字典树知识总结
2021/04/25 Python