Python 元类实现ORM

news2024/12/24 8:38:48

目录

ORM概念

__new__、__init__、__call__的介绍

通过元类简单实现ORM中的insert功能

抽取到基类中


ORM概念

ORM(Object Ralational Mapping,对象关系映射)用来把对象模型表示的对象映射到基于 SQL  的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQL 语句打交道,只需简单的操作实体对象的属性和方法。

一个句话理解就是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句。

示例:

class User(父类省略):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    ...省略...


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
u.save()
# 对应如下sql语句
# insert into User (username,email,password,uid)
# values ('Michael','test@orm.org','my-pwd',12345)

__new__、__init__、__call__的介绍

在讲使用元类创建ORM之前,必须了解__new__这个内置方法的作用。

__new__方法负责创建一个实例对象,在对象被创建的时候调用该方法它是一个类方法。__new__方法在返回一个实例之后,会自动的调用__init__方法,对实例进行初始化。如果__new__方法不返回值,或者返回的不是实例,那么它就不会自动的去调用__init__方法。

__init__ 方法负责将该实例对象进行初始化,在对象被创建之后调用该方法,在__new__方法创建出一个实例后对实例属性进行初始化。__init__方法可以没有返回值。

__call__方法其实和类的创建过程和实例化没有多大关系了,定义了__call__方法才能被以函数的方式执行。

class A(object):
    def __call__(self):
        print "__call__ be called"

a = A()
a()    # 输出:__call__ be called 

通过元类简单实现ORM中的insert功能

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # 删除这些已经在字典中存储的属性
        for k in mappings.keys():
            attrs.pop(k)

        # 将之前的uid/name/email/password以及对应的对象引用、类名字
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class User(metaclass=ModelMetaclass):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    # 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储
    # 以上User类中有 
    # __mappings__ = {
    #     "uid": ('uid', "int unsigned")
    #     "name": ('username', "varchar(30)")
    #     "email": ('email', "varchar(30)")
    #     "password": ('password', "varchar(30)")
    # }
    # __table__ = "User"
    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)    # 设置属性值

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0])
            args.append(getattr(self, k, None))

        args_temp = list()
        for temp in args:
            # 判断入如果是数字类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)    # 处理字符串类型的数据
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        print('SQL: %s' % sql)


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
# print(u.__dict__)
u.save()

输出结果

Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')

抽取到基类中

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v     # 将提取的内容存放在一个字典中

        # 删除这些已经在字典中存储的属性
        for k in mappings.keys():
            attrs.pop(k)

        # 将之前的uid/name/email/password以及对应的对象引用、类名字
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class Model(object, metaclass=ModelMetaclass):
    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)  # 将name,value设置为实例属性

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            # fields.append(v[0])     # v为类属性的元组
            fields.append(k)    # 如果使用v[0],那就是取元组中的uid、username等为数据库表中字段名,不利于理解,直接取类属性的变量名
            args.append(getattr(self, k))   # k为类属性的变量名

        args_temp = list()
        for temp in args:
            # 判断入如果是数字类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        print('SQL: %s' % sql)


class User(Model):      # User对应数据库中的表名
    # uid = ('uid', "int unsigned")
    # username = ('name', "varchar(30)")
    # email = ('email', "varchar(30)")
    # password = ('password', "varchar(30)")
    uid = ("int unsigned",)     # 数据库字操作的字段以这里的字段名为主,实例化传的变量名,主要用来匹配各个字段对应的值,可以多但不能少,否则报错
    username = ("varchar(30)",)
    email = ("varchar(30)",)
    password = ("varchar(30)",)


u = User(uid=12345, username='Michael', email='test@orm.org', password='my-pwd', phone=12345678900)
# print(u.__dict__)
u.save()

输出结果

Found mapping: uid ==> ('int unsigned',)
Found mapping: username ==> ('varchar(30)',)
Found mapping: email ==> ('varchar(30)',)
Found mapping: password ==> ('varchar(30)',)
SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')

通过上面的示例,我们可以看出用元类创建API是非常好的选择,使用元类的编写虽然很复杂,但使用者可以非常简洁的调用API。


-事必有法,然后有成- 最后祝大家早日达到测试的天花板!



 以下是我收集到的比较好的学习教程资源,虽然不是什么很值钱的东西,如果你刚好需要,可以评论区,留言【777】直接拿走就好了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/717905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

隔断推拉门滑动不畅常见的原因和解决方法

隔断推拉门滑动不畅常见的原因和解决方法如下: 1. 滑轨污秽:如果滑轨上有灰尘、油垢或杂物积聚,会影响推拉门的滑动效果。解决方法是定期清洁滑轨,使用吸尘器或刷子清除污垢,并用湿布擦拭干净。 2. 滑轨损坏&#xff1…

Git---分支管理

文章目录 前言一、理解分支二、创建分支三、切换分支四、合并分支五、删除分支六、合并冲突七、分支管理策略八、分支策略九、bug分支十、删除临时分支总结 前言 本篇博客开始介绍Git的杀手级功能之一 : 分支.分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习C的时候,另…

MySQL学习基础篇(六)---多表查询

MySQL学习基础篇(六)—多表查询 对于多表查询的理解: 多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。 前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是…

浅析EasyCVR视频技术与AR实景智能管理平台在智慧厂区中的应用

一、背景分析 新型智慧厂区是运用人工智能、大数据、物联网和设备监控技术加强厂区安保和信息管理。通过先进技术,保障厂区生产运营安全,同时减少生产线上的人工干预、及时正确地采集各类生产数据,以及合理的生产计划编排与生产进度&#xff…

线性规划的对偶问题(The Dual of LP)

线性规划的对偶问题(The Dual of LP) 对偶理论是线性规划中最重要的理论之一,是深入了 解线性规划问题结构的重要理论基础。同时,由于问题提 出本身所具有的经济意义,使得它成为对线性规划问题系 统进行经济分析和敏感…

原生 js点击空白处 关闭遮罩层(关闭某个div)

如图所示 点击空白处关闭此div document.addEventListener(click, function(e) {//监听页面点击事件var screenDoc document.querySelector(.controlHead);//需要关闭的divvar control document.querySelector(.control);//设置的按钮if (screenDoc && !screenDoc.co…

基于TF-IDF+Tensorflow+pyQT+孪生神经网络的智能聊天机器人(深度学习)含全部工程源码及模型+训练数据集

目录 前言总体设计系统整体结构图系统流程图孪生神经网络结构图 运行环境Python 环境TensorFlow 环境 模块实现1. 数据预处理2. 创建模型并编译3. 模型训练及保存4. 模型应用 系统测试1. 训练准确率2. 测试效果3. 模型应用 工程源代码下载其它资料下载 前言 本项目利用TF-IDF&…

【Docker、Dockerfile】使用Dockerfile创建镜像并运行容器

创建SpringBoot项目 创建一个Springboot的Java项目,然后打包成可运行的Jar 编写Dockerfile文件 在项目文件夹下创建Dockerfile文件,内容如下 # 定义用于构建镜像的 JDK 版本参数,可以在构建过程中通过 --build-arg 选项传递给 docker bui…

如何用adb命令统计app冷启耗时

第一步:杀掉App,然后重启,过滤Displayed日志,找到App启动的第一个Activity; adb logcat | grep Displayed 第二步:再次杀掉App,使用adb shell am start命令来启动app: adb shell a…

podman容器的使用

podman 安装 如果你想在rhel系统中玩podman,必须是rhel8.2版本以上。podman版本是1.9.3。从centos8.2开始默认情况下,除了最小化安装之外,系统都会默认安装podman。 如果你使用rhel8.2以上的版本,那么就直接安装podman就可以了。…

关于多grib文件合并以及netcdf和grib的方法

前言 之前在使用WRF做敏感性试验时,曾修改初始场的grib文件,并涉及到多个grib文件合并的情况,下面介绍一些好用的工具处理netcdf和grib 1、 grib_copy 官网:https://confluence.ecmwf.int/display/ECC/grib_copy 在Linux系统上…

从零实现深度学习框架——Seq2Seq模型尝试优化

引言 本着“凡我不能创造的,我就不能理解”的思想,本系列文章会基于纯Python以及NumPy从零创建自己的深度学习框架,该框架类似PyTorch能实现自动求导。 💡系列文章完整目录: 👉点此👈 要深入理解深度学习,从零开始创建的经验非常重要,从自己可以理解的角度出发,尽…

【操作系统核心概念】进程管理和进程调度

文章目录 前言1. 什么是操作系统2. 操作系统的定位3. 进程管理3.1 什么是进程/任务 (Process/Task)3.2 进程管理怎么做的3.3 进程控制块抽象PCB (Process Control Block)3.3.1 进程调度属性 4. 内存分配 ---- 内存管理 (Memory Manage)5. 进程间通信 (Inter Process Communicat…

带你看懂串口服务器

1、背景 串口服务器提供串口转网络功能,能够将RS-232/485/422串口转换成TCP/IP网络接口,实现RS-232/485/422串口与TCP/IP网络接口的数据双向透明传输。使得串口设备能够立即具备TCP/IP网络接口功能,连接网络进行数据通信,极大的扩…

Mac 脚本编辑器 (scriptEditor)

文章目录 1、打开 scriptEditor2、编写脚本3、执行脚本 1、打开 scriptEditor command 空格 输入 scriptEditor 2、编写脚本 脚本语言可以保存问文件,可以重复编辑 如下,每次打开一个新终端,执行新命令 tell application "Terminal&…

冒泡排序(附图详解)

目录 什么是冒泡排序? 冒泡排序的算法思想 算法图解 参考代码 运行结果 优化代码 运行结果 什么是冒泡排序? 将两个相邻的元素进行比较,如果前面的元素大(从小到大排序),就交换两个元素,…

ESP32设备驱动-ICM-20948 运动跟踪驱传感器驱动

ICM-20948 运动跟踪驱传感器驱动 文章目录 ICM-20948 运动跟踪驱传感器驱动1、ICM-20948介绍2、硬件准备3、软件准备4、驱动实现1、ICM-20948介绍 ICM-20948 是功耗非常低的 9 轴运动跟踪设备,非常适合智能手机、平板电脑、可穿戴传感器和物联网应用。 功率是现有 9 轴设备的…

02、PIC16F877的ADC模块

02、PIC16F877的ADC模块 一、ADC的一般配置步骤 二、ADC的软件滤波 三、性能提高 四、A/D器件推荐

【Java基础教程】(一)入门介绍篇 · 上:快速掌握核心概念,开启Java世界的探索之旅!这篇Java入门宝典助你翱翔~

Java基础教程之入门介绍 上 本节学习目标1️⃣ Java发展简史1.1 诞生1.2 发展1.3 分支 2️⃣ 特征3️⃣ 代码执行过程🌾 总结 本节学习目标 清楚Java语言的发展历史以及语言主要特点;清楚Java代码从源文件到解释执行的大致过程;理解Java实现…

react—Hook(1)

1. useState——见react语法(2) 2. useEffect——见react语法(2) 3. useRef—获取页面元素 用来获取页面中的元素,这样就可以对这个元素进行操作。获取元素需要使用.current才能获取到页面中的具体元素。 定义&…