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

本文记录下自己使用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模型的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python ORM框架Peewee用法详解

    之前在学Django时,发现它的模型层非常好用,把对数据库的操作映射成对类.对象的操作,避免了我们直接写在Web项目中SQL语句,当时想,如果这个模型层可以独立出来使用就好了,那我们平台操作数据库也可以这么玩了,我不喜欢写SQL语句. 后来才知道,原来这个叫ORM(Object Relational Mapping,对象关系映射),在Python下面有很多这样的类库,如SQLObject.Storm.peewee和SQLAlchemy. 这里就给你们介绍一下Peewee的基本使用,因为它非常的轻

  • Python colormap库的安装和使用详情

    colormap库是Python中的一个对颜色进行处理的第三方库,常用于对RGB(red,green,blue三原色的缩写,真彩图像)颜色的转换,生成颜色图等. pypi文档地址:https://pypi.org/project/colormap/ 一.安装colormap pip install -i https://pypi.tuna.tsinghua.edu.cn/simple easydev pip install colormap colormap库依赖于easydev库,需要先安装e

  • python GUI库图形界面开发之PyQt5表单布局控件QFormLayout详细使用方法与实例

    PyQt5布局控件QFormLayout简介 QFormLayout是label-field式的表单布局,顾明思议,就是实现表单方式的布局,表单是提示用户进行交互的一种模式,主要有两列组成,第一列用于显示信息,给用户提示,一般叫做label域,第二列需要用户进行选择或输入,一般叫做field域,label与field的关系就是label关联field QFormLayout表单布局的使用实例 import sys from PyQt5.QtWidgets import QApplication

  • 详解Python中的format格式化函数的使用方法

    format函数实现字符串格式化的功能 基本语法为: 通过 : 和 {} 来控制字符串的操作 一.对字符串进行操作 1. 不设置指定位置,按默认顺序插入 ①当参数个数等于{}个数的时候 str_1 = "小明{}小美,可是小美{}小明".format("喜欢", "不喜欢") print(str_1) 运行结果: ②当参数个数大于{}个数的时候 str_1 = "小明{}小美,可是小美{}小明".format("喜欢

  • python实现发送form-data数据的方法详解

    本文实例讲述了python实现发送form-data数据的方法.分享给大家供大家参考,具体如下: 源代码 -----------------------------279361243530614 Content-Disposition: form-data; name="parent_dir" / -----------------------------279361243530614 Content-Disposition: form-data; name="file&qu

  • Python3+SQLAlchemy+Sqlite3实现ORM教程

    一.Sqlite3.SQLAlchemy安装 Sqlite3是Python3标准库不需要另外安装,只需要安装SQLAlchemy即可.本文sqlalchemy版本为1.2.12 pip install sqlalchemy 二.ORM操作 除了第一步创建引擎时连接URL不一样,其他操作其他mysql等数据库和sqlite都是差不多的. 2.1 创建数据库连接格式说明 sqlite创建数据库连接就是创建数据库,而其他mysql等应该是需要数据库已存在才能创建数据库连接:建立数据库连接本文中有时会称

  • python 实现format进制转换与删除进制前缀

    10进行十进制,十六进制,八进制,二进制的转换: (#:保留进制前缀) 对于带着进制前缀的,如"0x""0o""0b",可以直接在后面加上'x''o''b'进行删除: 其他的一些小技巧: format(num, ",") 快速分割千分位 数字是补全多少位,^居中 >右对齐 <左对齐 *^10 ⟹ 一共10位,居中,剩下的用*补齐 补充:Python 进制转换函数及前缀截取 "人生苦短,我用Python&q

  • Python 格式化输出_String Formatting_控制小数点位数的实例详解

    问题概述: 有时候在使用print函数输出时,往往需要不断地切换字符串和变量,操作起来很不方便,需要不断地打引号和逗号.比如: firstName = 'Bob' lastName = 'Dylan' print('你的名字是 ', firstName, '你的姓是', lastName) 好在我们可以用%来代替变量名,最后再依次序解释每个%对应的变量是什么就可以了. firstName = 'Bob' lastName = 'Dylan' print('你的名字是%s, 你的姓是%s' % (

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

    本文记录下自己使用Python实现一个简易的ORM模型 使用到的知识 1.元类 2.描述器 元类 对于元类,我的理解其实也便较浅,大概是这个意思 所有的类都是使用元类来进行创建的,而所有的类的父类中必然是object(针对Python3),Python中的元类只有一个(type),当然这里不包含自定义元类 下面我们来看下类的创建 class Test: # 定义一个类 pass Test1 = type("Test2",(object,),{"name":"

  • 使用 Python 写一个简易的抽奖程序

    不知道有多少人是被这个头图骗进来的:) 事情的起因是这样的,上周有同学问小编,看着小编的示例代码敲代码,感觉自己也会写了,如果不看的话,七七八八可能也写的出来,但是一旦自己独立写一段程序,感觉到无从下手. 其实这个很正常,刚开始学习写代码,都是跟着别人的套路往下写,看的套路少,很难形成自己的套路,这就和做数学题是一样的,做一道题就想会所有的题目,这个可能性微乎其微,都是通过大量的练习来摸索到自己的套路. 正好快过年了,各个公司都会搞一些抽奖活动,小编今天就来聊一下,如果要写一个简单的抽奖程序,小

  • 用Python写一个简易版弹球游戏

    我们前面讲了几篇关于类的知识点,为了让大家更好的掌握类的概念,并灵活的运用这些知识,我写了一个有趣又好玩的弹球的游戏,一来可以把类的知识融会一下,二来加深对Python的兴趣.你会发现哎呀Python写小游戏还是蛮方便的,蛮有意思的~~ 先看一下我们的最终效果图 我们分9步来讲解如何写这个小游戏 1.创建游戏的主界面 我们用Python的内置模块Tkinter来完成了,它是Python的标准GUI工具包,可以非常方便在制作GUI小工具,因为是跨平台的,可以方便的在win和linux下运行,我们用

  • 基于Python实现一个简易的数据管理系统

    目录 创建mysql数据表 增删改查 启动应用  为了方便的实现记录数据.修改数据没有精力去做一个完整的系统去管理数据.因此,在python的控制台直接实现一个简易的数据管理系统,包括数据的增删改查等等.只需要在控制台层面调用相应的功能调用查询.修改等功能,这里记录一下实现过程. 创建mysql数据表 使用比较熟悉的数据库客户端来进行操作,这里使用的是navicate客户端来创建好相应的数据表. 创建数据库并指定编码字符集. CREATE DATABASE `data_boc` CHARACTE

  • 利用Python实现一个简易的截图工具

    这是工作期间同事想要个截完图之后可以显示并且永远前置的截图小工具(即不会被其他程序覆盖)直接上代码: # # -*- coding: utf-8 -*- import tkinter as tk import pyautogui import tkinter from PIL import ImageTk from PIL import Image root = tk.Tk() root.wm_attributes('-topmost', 1) root.overrideredirect(Tru

  • 教你使用Python实现一个简易版Web服务器

    目录 一.简介 二.Web服务器基础概念 三.Python网络编程库 四.实现简易Web服务器 1.使用socket库创建服务器套接字. 2.绑定服务器IP地址和端口. 3.监听客户端连接. 4.接受客户端连接并处理请求. 五.处理HTTP请求 六.返回静态文件 1.根据请求URL读取文件内容. 2.根据文件内容构建HTTP响应. 七.测试与优化 八.总结及拓展 九.补充:多线程处理客户端请求 一.修改处理客户端请求的函数 二.使用多线程处理客户端请求 三.完整的多线程Web服务器代码 一.简介

  • Python使用描述器实现ORM模型的方法详解

    目录 1.__set__方法:设置属性 2.__get__方法:访问属性 3.__delete__方法:删除属性 4.描述器实现ORM模型: 总结 访问或者修改描述器对象的属性时无法触发__setattr__等方法,只会触发描述器类内部的__set__,__get__,__delete__方法. ORM模型:类名对应表名,对象对应的数据行,类属性对应数据行的各字段,有几个表字段,就绑定几个类属性:往表中增加数据就是创建对象,每创建一个对象,就是增加一行数据记录. ORM框架的功能: 1.建立模型

  • python利用元类和描述器实现ORM模型的详细步骤

    ORM模型: ORM模型对于后端开发来说肯定是不陌生的,包括很多后端框架比如django,现在都自带这个模型了 ORM(Object Relational Mapping)对象关系映射 Python中的类与数据库之间的映射,对数据的操作就不用编写SQL语言了,因为都封装好了,比如你想插入一条数据,你就直接创建一个对象即可, 类名 ------->>>> 数据库中的表名 属性 ------->>>> 数据库中的字段 对象 ------->>>

  • 基于python的Tkinter实现一个简易计算器

    本文实例介绍了基于python的Tkinter实现简易计算器的详细代码,分享给大家供大家参考,具体内容如下 第一种:使用python 的 Tkinter实现一个简易计算器 #coding:utf-8 from Tkinter import * import time root = Tk() def cacl(input_str): if "x" in input_str: ret = input_str.split("x") return int(ret[0]) *

  • 详解用python -m http.server搭一个简易的本地局域网

    工作时同事间几mb小文件的传输,一般使用QQ或者微信就足够了,但当传输文件几百MB或者几十G时,这种方法的效率就显得不足了.本篇就是简单说明一个python小功能,让大家能利用python方便的搭建一个本地局域网.跟同事测试时,速度轻松达到800mb/s. 搭建只需三步就可以: 1.设置python路径为环境变量 2.命令行输入python -m http.server 8888来搭建局域网 3.使用本机的ip地址进行访问 接下来我们一步一步看: 设置python路径为环境变量 1.先找到自己p

随机推荐