如何使用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实现的udp协议Server和Client代码实例
Jun 04 Python
Python获取当前页面内所有链接的四种方法对比分析
Aug 19 Python
matplotlib实现热成像图colorbar和极坐标图的方法
Dec 13 Python
python实现彩色图转换成灰度图
Jan 15 Python
详解用python写网络爬虫-爬取新浪微博评论
May 10 Python
Django文件存储 自己定制存储系统解析
Aug 02 Python
python笔记_将循环内容在一行输出的方法
Aug 08 Python
python numpy之np.random的随机数函数使用介绍
Oct 06 Python
Python处理mysql特殊字符的问题
Mar 02 Python
python thrift 实现 单端口多服务的过程
Jun 08 Python
python 读取、写入txt文件的示例
Sep 27 Python
Python实现科学占卜 让视频自动打码
Apr 09 Python
用python删除文件夹中的重复图片(图片去重)
May 12 #Python
Pyhton模块和包相关知识总结
python 下划线的多种应用场景总结
May 12 #Python
超级详细实用的pycharm常用快捷键
pycharm 如何查看某一函数源码的快捷键
教你使用Pandas直接核算Excel中快递费用
用python开发一款操作MySQL的小工具
May 12 #Python
You might like
php打包网站并在线压缩为zip
2016/02/13 PHP
yii2实现 &quot;上一篇,下一篇&quot; 功能的代码实例
2017/02/04 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
动态加载js文件 document.createElement
2006/10/14 Javascript
模拟电子签章盖章效果的jQuery插件源码
2013/06/24 Javascript
js window.open弹出新的网页窗口
2014/01/16 Javascript
js unicode 编码解析关于数据转换为中文的两种方法
2014/04/21 Javascript
jQuery中checkbox反复调用attr('checked', true/false)只有第一次生效的解决方法
2016/11/16 Javascript
详解AngularJS中的表单验证(推荐)
2016/11/17 Javascript
JS时间控制实现动态效果的实例讲解
2017/07/31 Javascript
简单实现js上传文件功能
2017/08/21 Javascript
详解Vue 全局变量,局部变量
2019/04/17 Javascript
npm 语义版本控制详解
2019/09/10 Javascript
javascript刷新父页面方法汇总详解
2019/10/10 Javascript
详解使用 pyenv 管理多个版本 python 环境
2017/10/19 Python
Python 数据处理库 pandas 入门教程基本操作
2018/04/19 Python
python 字典 按key值大小 倒序取值的实例
2018/07/06 Python
python高阶爬虫实战分析
2018/07/29 Python
Python函数any()和all()的用法及区别介绍
2018/09/14 Python
Django中Middleware中的函数详解
2019/07/18 Python
树莓派4B+opencv4+python 打开摄像头的实现方法
2019/10/18 Python
python、Matlab求定积分的实现
2019/11/20 Python
带你彻底搞懂python操作mysql数据库(cursor游标讲解)
2020/01/06 Python
计算pytorch标准化(Normalize)所需要数据集的均值和方差实例
2020/01/15 Python
keras 实现轻量级网络ShuffleNet教程
2020/06/19 Python
Python内存泄漏和内存溢出的解决方案
2020/09/26 Python
俄罗斯街头服装品牌:Black Star Wear
2017/03/01 全球购物
alice McCALL官网:澳大利亚时尚品牌
2020/11/16 全球购物
Araks官网:纽约内衣品牌
2020/10/15 全球购物
Ruby中的保护方法和私有方法与一般面向对象程序设计语言的一样吗
2013/05/01 面试题
文明美德伴我成长演讲稿
2014/05/12 职场文书
《改造我们的学习》心得体会
2014/11/07 职场文书
2015年高三年级组工作总结
2015/07/21 职场文书
推普标语口号大全
2015/12/26 职场文书
学校标语口号大全
2015/12/26 职场文书
centos8安装nginx1.9.1的详细过程
2021/08/02 Servers