Python简单ORM实现:不使用元类的灵活数据操作与查询构建【第29篇—python:ORM】

news2025/1/10 18:39:11

文章目录

  • 不使用元类的简单ORM实现
    • Field类
    • Compare类
    • Model类
    • Query类
    • 示例使用
    • 扩展查询功能
      • 支持 LIMIT 和 OFFSET
      • 支持 GROUP BY 和 HAVING
    • 示例用法
    • 总结

不使用元类的简单ORM实现

在 Python 中,ORM(Object-Relational Mapping)是一种将对象和数据库之间的映射关系进行转换的技术,使得通过面向对象的方式来操作数据库更加方便。通常,我们使用元类(metaclass)来实现ORM,但是本文将介绍一种不使用元类的简单ORM实现方式。

Field类

首先,我们定义一个Field类,用于表示数据库表中的字段。这个类包含字段的名称和类型等信息,并且支持一些比较操作,以便后续构建查询条件。

class Field:
    def __init__(self, **kwargs):
        self.name = kwargs.get('name')
        self.column_type = kwargs.get('column_type')

    def __eq__(self, other):
        return Compare(self, '=', other)

    # 其他比较操作略...

Compare类

为了构建查询条件,我们引入了一个Compare类,用于表示字段之间的比较关系。它可以支持链式操作,构建复杂的查询条件。

class Compare:
    def __init__(self, left: Field, operation: str, right: Any):
        self.condition = f'`{left.name}` {operation} "{right}"'

    def __or__(self, other: "Compare"):
        self.condition = f'({self.condition}) OR ({other.condition})'
        return self

    def __and__(self, other: "Compare"):
        self.condition = f'({self.condition}) AND ({other.condition})'
        return self

Model类

接下来,我们定义Model类,表示数据库中的表。该类通过Field类的实例来定义表的字段,并提供了插入数据的方法。

class Model:
    def __init__(self, **kwargs):
        _meta = self.get_class_meta()

        for k, v in kwargs.items():
            if k in _meta:
                self.__dict__[k] = v

    @classmethod
    def get_class_meta(cls) -> Dict:
        if hasattr(cls, '_meta'):
            return cls.__dict__['_meta']
        _meta = {}

        for k, v in cls.__dict__.items():
            if isinstance(v, Field):
                if v.name is None:
                    v.name = k
                name = v.name
                _meta[k] = (name, v)

        table = cls.__dict__.get('__table__')
        table = cls.__name__ if table is None else table
        _meta['__table__'] = table

        setattr(cls, '_meta', _meta)

        return _meta

    def insert(self):
        _meta = self.get_class_meta()
        column_li = []
        val_li = []

        for k, v in self.__dict__.items():
            field_tuple = _meta.get(k)
            if field_tuple:
                column, field = field_tuple
                column_li.append(column)
                val = str(v) if field.column_type == 'INT' else f'"{str(v)}"'
                val_li.append(val)

        sql = f'INSERT INTO {_meta["__table__"]} ({",".join(column_li)}) VALUES ({",".join(val_li)});'
        print(sql)

Query类

最后,我们实现了Query类,用于构建数据库查询。这个类支持链式调用,可以设置查询条件、排序等。

class Query:
    def __init__(self, cls: Model):
        self._model = cls
        self._order_columns = None
        self._desc = ''
        self._meta = self._model.get_class_meta()
        self._compare = None
        self.sql = ''

    def _get(self) -> str:
        sql = ''

        if self._compare:
            sql += f' WHERE {self._compare.condition}'

        if self._order_columns:
            sql += f' ORDER BY {self._order_columns}'

        sql += f' {self._desc}'
        return sql

    def get(self, *args: Field) -> List[Model]:
        sql = self._get()
        table = self._meta['__table__']

        column_li = []

        if len(args) > 0:
            for field in args:
                column_li.append(f'`{field.name}`')
        else:
            for v in self._meta.values():
                if type(v) == tuple and isinstance(v[1], Field):
                    column_li.append(f'`{v[0]}`')

        columns = ",".join(column_li)
        sql = f'SELECT {columns} FROM {table} {sql}'
        self.sql = sql
        print(self.sql)

    def order_by(self, columns: Union[List, str], desc: bool = False) -> "Query":
        if isinstance(columns, str):
            self._order_columns = f'`{columns}`'
        elif isinstance(columns, list):
            self._order_columns = ','.join([f'`{x}`' for x in columns])

        self._desc = 'DESC' if desc else ''
        return self

    def where(self, compare: "Compare") -> "Query":
        self._compare = compare
        return self

示例使用

在这里插入图片描述

现在,我们可以定义一个模型类,并使用这个简单的ORM实现进行数据操作。

class User(Model):
    name = Field()
    age = Field()

# 插入数据
user = User(name='Tom', age=24)
user.insert()

# 构建查询条件并查询数据
User.query().where((User.name == 'Tom') & (User.age >= 20)).order_by('age').get()

这样,我们就完成了一个不使用元类的简单ORM实现。尽管相较于使用元类的方式,代码结构更为简单,但在实际应用中,根据项目需求和团队的约定,选择合适的实现方式是很重要的。
我们已经介绍了一个基于 Python 的简单 ORM 实现,它不依赖于元类。在这一部分,我们将继续探讨这个实现,深入了解查询构建和更复杂的用法。

扩展查询功能

我们的查询功能还比较简单,为了更好地支持复杂查询,我们可以添加更多的查询方法和条件。

支持 LIMIT 和 OFFSET

class Query:
    # ...

    def limit(self, num: int) -> "Query":
        self.sql += f' LIMIT {num}'
        return self

    def offset(self, num: int) -> "Query":
        self.sql += f' OFFSET {num}'
        return self

支持 GROUP BY 和 HAVING

class Query:
    # ...

    def group_by(self, columns: Union[List, str]) -> "Query":
        if isinstance(columns, str):
            columns = [columns]
        self.sql += f' GROUP BY {",".join([f"`{x}`" for x in columns])}'
        return self

    def having(self, condition: Compare) -> "Query":
        self.sql += f' HAVING {condition.condition}'
        return self

示例用法

class User(Model):
    name = Field()
    age = Field()

# 插入数据
user = User(name='Tom', age=24)
user.insert()

# 构建查询条件并查询数据
query = User.query().where((User.name == 'Tom') & (User.age >= 20)).order_by('age').limit(1).offset(0)
query.get(User.name, User.age)  # 仅查询指定字段

# 更复杂的查询
query = User.query().group_by('age').having((User.age > 20) & (User.age < 30)).order_by('age').limit(10).offset(0)
query.get(User.age, User.count(User.name))  # 查询年龄在20到30之间的用户数量

通过引入额外的查询功能,我们使得这个简单的 ORM 实现更加强大和灵活。

总结

在这个系列的文章中,我们通过不使用元类的方式,实现了一个简单的 Python ORM。我们定义了 Field 类表示数据库字段,Model 类表示数据库表,以及 Query 类用于构建和执行查询。通过这个实现,我们可以方便地进行数据操作,构建灵活的查询条件,而不需要深入理解元类的概念。

然而,这个简单的 ORM 仍然有一些局限性,例如不支持复杂的表关联等功能。在实际项目中,选择使用元类的 ORM 实现或其他成熟的 ORM 框架取决于项目的需求和团队的技术选型。希望这个实现能够为你提供一种不同的思路,促使更多的思考和探讨。
在这里插入图片描述

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

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

相关文章

什么是泛域名证书?有免费的吗?

泛域名证书&#xff08;Wildcard SSL Certificate&#xff09;是一种用于加密多个子域名的SSL证书。与传统的SSL证书只能覆盖单个域名或特定子域不同&#xff0c;泛域名证书具有更广泛的适用性&#xff0c;可以涵盖一个域名下的所有子域。 泛域名证书的主要特点是通配符&#x…

华为数通方向HCIP-DataCom H12-831题库(判断题:1-20)

第01题 为了加快IS-IS网络中链路故障的感知速度,可以将IS-IS与BFD联动 正确 错误 答案:正确 解析: OSPF和IS-IS都可以设置与BFD联动加速链路故障检测 ,使用BFD时,可以实现毫秒级别的链路切换,所以使用IS–IS与BFD联动,可以加快IS–IS的感知速度 第02题 在OSPF中ABR会将…

引入企业社区打造员工互动环境

作为一家富有活力和创新精神的企业&#xff0c;应始终致力于提供一个积极、紧密和互动的工作环境&#xff0c;以促进员工之间的合作与讨论。引入企业社区&#xff0c;打造了一个集积分商城、互动板块和意见箱等功能于一体的社区空间&#xff0c;旨在进一步加强企业内部的沟通与…

【Linux】各目录说明

【常见目录说明】 目录 /bin 存放二进制可执行文件(ls,cat,mkdir等)&#xff0c;常用命令一般都在这里。 /etc 存放系统管理和配置文件 /home 存放所有用户文件的根目录&#xff0c;是用户主目录的基点&#xff0c;比如用户user的主目录就是/home/user&#xff0c;可以…

【css】渐变效果

css渐变效果 使用 CSS 渐变可以在两种颜色间制造出平滑的渐变效果。 用它代替图片&#xff0c;可以加快页面的载入时间、减小带宽占用。同时&#xff0c;因为渐变是由浏览器直接生成的&#xff0c;它在页面缩放时的效果比图片更好&#xff0c;因此你可以更加灵活、便捷的调整页…

Google的Ndk-Sample学习笔记之一(hello-jniCallback)

前言: 近段时间因为项目的需求,需要使用JNI,所以下载了Google的Ndk-Sample学习下,准备记录 下来,留给后期自己查看 问题点一:JNI_OnLoad方法必须返回JNI的版本 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env;memset(&g_ctx, 0, sizeof(g_…

鸿蒙开发笔记(五):状态管理,组件状态管理 @State @Prop @Link

如果希望构建一个动态的、有交互的界面&#xff0c;就需要引入“状态”的概念。 在声明式UI编程框架中&#xff0c;UI是程序状态的运行结果&#xff0c;用户构建了一个UI模型&#xff0c;其中应用的运行时的状态是参数。当参数改变时&#xff0c;UI作为返回结果&#xff0c;也…

斯坦福 Stats60:21 世纪的统计学:第十五章到第十八章

第十五章&#xff1a;比较均值 原文&#xff1a;statsthinking21.github.io/statsthinking21-core-site/comparing-means.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 我们已经遇到了许多情况&#xff0c;我们想要询问样本均值的问题。在本章中&#xff0c;我们…

当前vscode环境下 多进程多线程运行情况探究

我的代码 其中在“打开图片时”、“进入子进程之前”、“子进程join前”、“进入子进程区域后”&#xff0c;“子进程join后”、“进入子线程区域后”分别打印了进程线程的编号和数量。 # -*- coding: utf-8 -*-# Form implementation generated from reading ui file test2.…

嵌入式-Stm32-江科大基于标准库的GPIO通用输入输出口

文章目录 一&#xff1a;GPIO输入输出原理二&#xff1a;GPIO基本结构三&#xff1a;GPIO位结构四&#xff1a;GPIO的八种模式道友&#xff1a;相信别人&#xff0c;更要一百倍地相信自己。 &#xff08;推荐先看文章&#xff1a;《 嵌入式-32单片机-GPIO推挽输出和开漏输出》…

virtualbox Ubuntu 网络连接

一、网络连接需求1—— 上网&#xff1a; 虚拟机默认的NAT连接方式&#xff0c;几乎不需要怎么配置&#xff0c;即可实现上网。 enp0s17以太网必须要开启&#xff0c;才能上网&#xff1b; 但是主机ping不通虚拟机&#xff0c;貌似可以ping 127.0.0.1; 二、主机和虚拟机相互p…

机器学习算法实战案例:LSTM实现多变量多步负荷预测

文章目录 1 数据处理1.1 数据集简介1.2 数据集处理 2 模型训练与预测2.1 模型训练2.2 模型多步预测2.3 结果可视化 答疑&技术交流机器学习算法实战案例系列 1 数据处理 1.1 数据集简介 实验数据集采用数据集6&#xff1a;澳大利亚电力负荷与价格预测数据&#xff0c;包括…

FFmpeg 入门

1. 编译 参考文档&#xff1a;FFmpeg编译和集成(FFmpeg开发基础知识)&#xff0c;重点注意这句话&#xff1a; 在MSYS2 Packages可以查到云仓库有哪些包&#xff0c;直接安装可节约大量时间。 注意&#xff1a;这个路径可自定义 吐槽 在看到这篇文章之前&#xff0c;花了大…

赋值运算符和关系运算符

赋值运算符和关系运算符 赋值运算符 分类 符号作用说明赋值int a 10&#xff0c; 将10赋值给变量a加后赋值a b&#xff0c;将a b的值赋值给a-减后赋值a - b&#xff0c;将a - b的值赋值给a*乘后赋值a * b&#xff0c;将a b的值赋值给a/除后赋值a / b&#xff0c;将a b的…

Java Chassis 3技术解密:注册中心分区隔离

原文链接&#xff1a;Java Chassis 3技术解密&#xff1a;注册中心分区隔离-云社区-华为云 注册中心负责实例的注册和发现&#xff0c;对微服务可靠运行起到举足轻重的作用。实例变更感知周期是注册中心最重要的技术指标之一。感知周期代表提供者的实例注册或者下线后&#xf…

uni书写TP6,环境7.3,随意二开,源码交付。APP小程序H5都有,UI美观

随着数字技术的迅猛发展和教育信息化的推进&#xff0c;智慧校园教务管理系统软件设计开发定制成为教育管理的重要举措。这样的系统可以利用先进的技术手段&#xff0c;提供全面的教务管理功能&#xff0c;提高教育管理的效率和质量。 课程管理&#xff1a;智慧校园教务管理系…

人力资源智能化管理项目(day01:基础架构拆解)

学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/frontlearningNotes 觉得有帮助的同学&#xff0c;可以点心心支持一下哈 一、基础架构拆解 1.拉取模板代码 git clone GitHub - PanJiaChen/vue-admin-template: a vue2.0 minimal admin template 项目名 2.core-js…

js 回文串

思路&#xff1a; 判断一个字符串是否为回文字符串的基本思路是比较字符串的正序和倒序是否相同。 两者相同&#xff0c;则该字符串是回文字符串&#xff0c;否则不是。 要实现这一思路&#xff0c;我们可以使用 JavaScript 字符串的一些方法。我是忽略了所有的空格和符号&…

岛屿问题(DFS)

DFS的基本结构 网格结构比二叉树结构稍微复杂一点&#xff0c;它其实是一种简化版的图的结构。要写好网格上的DFS遍历&#xff0c;我们首先要理解二叉树上的DFS遍历方法&#xff0c;再类比写出网格结构上的DFS遍历。我们写的二叉树DFS遍历一般是&#xff1a; public void tra…

我自己总结记忆的23种设计模式

1&#xff0c; 对23种设计模式&#xff0c;大家的第一个印象就是抽象繁琐&#xff0c;记不住&#xff01;&#xff01;不常用&#xff1f;&#xff1f; 其实设计模式是非常有用的&#xff0c;大家只要理解设计模式了&#xff0c;思想上就能有质的飞跃&#xff01; 但是&#…