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

作者:大渝 时间:2021-05-18 19:24:46 

本文记录下自己使用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)

来源:https://www.cnblogs.com/Pycainiao/p/14756759.html

标签:python,ORM
0
投稿

猜你喜欢

  • Python logging管理不同级别log打印和存储实例

    2021-08-27 19:12:29
  • 新手常见6种的python报错及解决方法

    2022-10-10 09:03:22
  • Python自动化测试之登录脚本的实现

    2022-11-18 07:30:50
  • pytorch+lstm实现的pos示例

    2023-08-11 22:02:10
  • python人工智能tensorflow构建卷积神经网络CNN

    2023-01-09 21:49:10
  • Mootools 1.2教程(6)——操纵HTML DOM元素

    2008-11-20 13:19:00
  • 利用python爬取有道词典的方法

    2021-07-08 04:42:05
  • 用openCV和Python 实现图片对比,并标识出不同点的方式

    2023-08-31 09:30:55
  • IPython 8.0 Python 命令行交互工具

    2022-10-24 09:17:54
  • python3爬虫中异步协程的用法

    2021-11-01 01:16:02
  • Python中的bytes类型用法及实例分享

    2022-12-19 01:50:42
  • python 判断是否为正小数和正整数的实例

    2022-06-12 01:26:04
  • VBScript中清除数组元素Erase语句

    2008-06-27 13:05:00
  • python中的装饰器该如何使用

    2021-01-17 20:40:45
  • Python科学画图代码分享

    2023-08-19 07:06:25
  • HTML和SEO基础知识:H标签全透视

    2010-09-21 16:45:00
  • Python全面解读高级特性切片

    2021-06-05 14:23:13
  • python的turtle库使用详解

    2023-07-09 02:38:17
  • pyinstaller打包opencv和numpy程序运行错误解决

    2023-02-23 02:42:54
  • 中英文双语导航菜单

    2007-05-11 17:04:00
  • asp之家 网络编程 m.aspxhome.com