如何使用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开发WebService系列教程之REST,web.py,eurasia,Django
Jun 30 Python
python实现定时同步本机与北京时间的方法
Mar 24 Python
Python中的命令行参数解析工具之docopt详解
Mar 27 Python
基于python requests库中的代理实例讲解
May 07 Python
基于python OpenCV实现动态人脸检测
May 25 Python
Python数据可视化教程之Matplotlib实现各种图表实例
Jan 13 Python
python爬虫租房信息在地图上显示的方法
May 13 Python
浅析Python 引号、注释、字符串
Jul 25 Python
Python for循环搭配else常见问题解决
Feb 11 Python
python统计文章中单词出现次数实例
Feb 27 Python
python GUI库图形界面开发之PyQt5控件数据拖曳Drag与Drop详细使用方法与实例
Feb 27 Python
一文带你了解Python 四种常见基础爬虫方法介绍
Dec 04 Python
用python删除文件夹中的重复图片(图片去重)
May 12 #Python
Pyhton模块和包相关知识总结
python 下划线的多种应用场景总结
May 12 #Python
超级详细实用的pycharm常用快捷键
pycharm 如何查看某一函数源码的快捷键
教你使用Pandas直接核算Excel中快递费用
用python开发一款操作MySQL的小工具
May 12 #Python
You might like
Syphon 使用方法
2021/03/03 冲泡冲煮
无数据库的详细域名查询程序PHP版(5)
2006/10/09 PHP
PHP中spl_autoload_register()和__autoload()区别分析
2014/05/10 PHP
对PHP语言认识上需要避免的10大误区
2014/06/12 PHP
laravel框架添加数据,显示数据,返回成功值的方法
2019/10/11 PHP
javascript 动态修改样式和层叠样式表代码
2010/04/27 Javascript
javascript各浏览器中option元素的表现差异
2011/04/07 Javascript
防止xss和sql注入:JS特殊字符过滤正则
2013/04/18 Javascript
window.location.href = window.location.href 跳转无反应 a超链接onclick事件写法
2013/08/21 Javascript
JavaScript实现拖拽网页内元素的方法
2015/04/15 Javascript
JQuery操作textarea,input,select,checkbox方法
2015/09/02 Javascript
Bootstrap Table使用整理(二)
2017/06/09 Javascript
微信小程序之页面拦截器的示例代码
2017/09/07 Javascript
angular4中关于表单的校验示例
2017/10/16 Javascript
vue中typescript装饰器的使用方法超实用教程
2019/06/17 Javascript
vue router总结 $router和$route及router与 router与route区别
2019/07/05 Javascript
el-select 下拉框多选实现全选的实现
2019/08/02 Javascript
React冒泡和阻止冒泡的应用详解
2020/08/18 Javascript
Python 返回汉字的汉语拼音
2009/02/27 Python
python读取html中指定元素生成excle文件示例
2014/04/03 Python
Python中在for循环中嵌套使用if和else语句的技巧
2016/06/20 Python
Python实现简单石头剪刀布游戏
2021/01/20 Python
对python3中, print横向输出的方法详解
2019/01/28 Python
十行代码使用Python写一个USB病毒
2019/06/21 Python
python 叠加等边三角形的绘制的实现
2019/08/14 Python
python中open函数的基本用法示例
2019/09/07 Python
解决Numpy中sum函数求和结果维度的问题
2019/12/06 Python
jQuery treeview树形结构应用
2021/03/24 jQuery
思想品德自我鉴定
2013/10/12 职场文书
俄语翻译实习生的自我评价分享
2013/11/06 职场文书
会计自荐书
2013/12/02 职场文书
自动化专业个人求职信范文
2013/12/30 职场文书
自考生自我评价分享
2014/01/18 职场文书
退休感言
2014/01/28 职场文书
3.15国际消费者权益日主题活动活动总结
2014/03/16 职场文书
学校文明单位申报材料
2014/05/06 职场文书