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

ORM模型:

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

类名 ------->>>> 数据库中的表名

属性 ------->>>> 数据库中的字段

对象 ------->>>> 数据库中的一行数据

大致就是上面的映射关系

ORM实现步骤:

1、利用描述器实现对数据库字段的类型、长度限制
2、实现模型类,也就是创建一张表并定义字段
3、利用元类实现映射关系

元类:

1.实例对象是通过类来创建,那类又是通过什么创建的呢?python中所有的类都是通过元类来创建类,且这个元类只有一个那就是type;

class Test:
    pass

t = Test()
print(type(Test))  #类是通过元类type创建
print(type(t))  #实例对象是通过类创建
#输出-----------------------
<class 'type'>
<class '__main__.Test'>

2.除了通过class关键字创建类,我们也可以通过元类type来创建类,type作为python的内置函数,其实他也是一个类,他有两个作用:
1.传入一个变量,返回这个变量的类型;
2.创建类时,第一个参数传入类名,第二个参数传入一个元祖,指定父类,第三个参数传入字典设置类的属性或方法;

#通过元类来创建类
test1 = type("test2",(object,),{"name":"xiaoming"})
t1 = test1()

print(type(test1))
print(type(t1))
#输出---------------
<class 'type'>
<class '__main__.test2'>

自定义元类:

1.自定义一个元类,这个元类必须继承type,用这个元类创建类时,必须使用metaclass指定他的元类;

class MetaClass(type):
    """自定义一个元类,这个元类必须继承type,用这个元类创建类时,必须使用metaclass指定他的元类"""
    def __new__(cls, name, bases, dic, *args, **kwargs):
        return super().__new__(cls, name, bases, dic)

class Test(metaclass = MetaClass):
    name = "xiaoming"

print(type(Test))
#输出----------------
<class '__main__.MetaClass'>  #可以看到此时这个类创建是用我们自定义的元类创建的,而不是type创建的

描述器:

1.描述器的定义,只要一个类中实现了__get__、set、__delete__中的一个或几个,这个类的实例就可以叫描述器,那描述器有神么作用呢,在我们给描述器设置属性时,一定会调用描述器里面的—set—方法,那我们在设置属性之前是不是可以在set方法里面,做一些手脚,比如校验这个属性长度,类型等等,如果不符合要求那就抛错,如果符合那就设置成功;

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 = "xiaoming"  #给描述器设置一个类属性
print(t.name)  #获取一个类属性
#输出---------------
设置属性的时候会被调用
获取属性的时候会被调用
xiaoming

利用描述器实现对数据库字段的类型、长度限制

设置属性一定会调用描述器里面的—set—方法,再设置属性之前校验这个属性长度,类型等等,如果不符合要求那就抛错,如果符合那就设置成功;

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 User():
"""这是一个模型类,相当于定义了一个表名是User的表,表有三个字段name,age,love"""
    name = CharFiled()
    age = IntFiled()
    love = CharFiled(length=50)

如果是这样的话我们给这个类实例化的时候,需要这样传值:

t1 = User()
t1.name = "xiaoming"
t1.age = 18
t1.love = "single"

如果我们需要一次性传入三个值得话,那就需要一个__init__来接收,但是模型类一般里面不定义init,所以我们可以给他找个父类,然后让他继承父类的init,这样我们就可以一次传入多个值,并且参数个数也不限定,优化如下:

class BaseMode():

    def __init__(self, **kwargs):
        """
        由于每一个模型类(也就是数据库表)的属性个数不一致,所以我们需要定义一个父类来进行定义初始化的属性
        :param kwargs:
        """
        for k, v in kwargs.items():   # 遍历传进来的所有属性
            setattr(self, k, v)   # 拿到这些属性后对self(也就是类本身)进行设置属性

class User(BaseMode):
"""第一个模型类"""
    name = CharFiled()
    age = IntFiled()
    love = CharFiled(length=50)

t1 = User(name= "xiaoming",age=18,love="xxx")#现在我们就可以一次传入多个值,并且参数个数也不限定

class Oder(BaseMode):
    """第二个模型类"""
    id = CharFiled()
    count = IntFiled()

t2 = Oder(id ="a1",count = 123)

利用元类实现模型类和数据库映射关系:

其实就是创建模型类的时候,提取模型类的类名作为表名,提取模型类的属性作为字段名,存储在模型类的类属性里,然后备用,代码如下:

class MetaClass(type):
    """自定义一个元类,这个元类必须继承type,用这个元类创建类时,必须使用meteclass指定他的元类"""
    def __new__(cls, name, bases, dic, *args, **kwargs):
        if name == "BaseMode":   # (User,Oder,BaserMode都指定了用这个元类)我们的目的是对模型类(User,Oder)进行提取操作,不对BaserMode进行操作,所以先判断类名是否为BaseMode,如果是则直接使用元类创建类,不需要提取
            return super().__new__(cls, name, bases, dic)
        else:
            table_name = name.lower()  # 提取表名,即模型类的类名,将表名变成小写
            filed_dic = {}   # 定义一个空的列表,用来装dic中(dic中的属性就是模型类里面我们写的字段)属于BaseFiled类型的属性,因为dic中会有其他创建类时自动生成的属性(例如__开头的一些),这些属性我们没必要去建立映射关系,所以需要将其剔除掉
            for k, v in dic.items():
                if isinstance(v,BaseFiled):
                    filed_dic[k] = v
            dic["t_name"] = table_name    # 给dic新加一个t_name的属性,将表名添加到dic中,实现类名与表名的映射关系
            dic["filed_dict"] = filed_dic  # 给dic新加一个filed_dict的属性,将属于BaseFiled类型的属性给添加到dic中,实现属性与字段的映射关系
        return super().__new__(cls, name, bases,dic)

**创建模型类的时候,利用给他指定元类,提取了表名和字段名,我们具体要怎么用呢?**我们可以在模型类的父类里面定义一个save方法,然后用这些字段写sql,在我们创建模型类的实例后,就调用这个save方法,即可向数据库插入一条数据了,如下:

class BaseMode(metaclass=MetaClass):

    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()))
        print(sql)
        return sql

class User(BaseMode):
    name = CharFiled()
    age = IntFiled()
    love = CharFiled(length=50)

class Oder(BaseMode):
    """第二个模型类"""
    id = CharFiled()
    count = IntFiled()

t2 = Oder(id ="a1",count = 123)#实例化模型类,并调用save方法,即可向数据库插入数据
t2.save()

t1 = User(name= "xiaoming",age=18,love="xxx")
t1.save()
#输出-----------------------
insert into oder values('a1', 123)
insert into user values('xiaoming', 18, 'xxx')

至此我们的一个简单的ORM模型就定义好了,我们只需要理解原理即可,因为一个成熟的后端框架都有现成的ORM模型,例如django和flask都自带的有,最后再附上全部代码,如果有帮到你,能点个赞吗,谢谢咯~~~

class MetaClass(type):
    """自定义一个元类,这个元类必须继承type,用这个元类创建类时,必须使用meteclass指定他的元类"""
    def __new__(cls, name, bases, dic, *args, **kwargs):
        if name == "BaseMode":   # (User,Oder,BaserMode都指定了用这个元类)我们的目的是对模型类(User,Oder)进行提取操作,不对BaserMode进行操作,所以先判断类名是否为BaseMode,如果是则直接使用元类创建类,不需要提取
            return super().__new__(cls, name, bases, dic)
        else:
            table_name = name.lower()  # 提取表名,即模型类的类名,将表名变成小写
            filed_dic = {}   # 定义一个空的列表,用来装dic中(dic中的属性就是模型类里面我们写的字段)属于BaseFiled类型的属性,因为dic中会有其他创建类时自动生成的属性(例如__开头的一些),这些属性我们没必要去建立映射关系,所以需要将其剔除掉
            for k, v in dic.items():
                if isinstance(v,BaseFiled):
                    filed_dic[k] = v
            dic["t_name"] = table_name    # 给dic新加一个t_name的属性,将表名添加到dic中,实现类名与表名的映射关系
            dic["filed_dict"] = filed_dic  # 给dic新加一个filed_dict的属性,将属于BaseFiled类型的属性给添加到dic中,实现属性与字段的映射关系
        return super().__new__(cls, name, bases,dic)

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 BaseMode(metaclass=MetaClass):

    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()))
        print(sql)
        return sql

class User(BaseMode):
    name = CharFiled()
    age = IntFiled()
    love = CharFiled(length=50)

class Oder(BaseMode):
    """第二个模型类"""
    id = CharFiled()
    count = IntFiled()

t2 = Oder(id ="a1",count = 123)
t2.save()

t1 = User(name= "xiaoming",age=18,love="xxx")
t1.save()

到此这篇关于python利用元类和描述器实现ORM模型的文章就介绍到这了,更多相关python ORM模型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python Django ORM与模型详解

    目录 一. 环境准备: 1.docker环境 docker环境如下: 2.安装mariadb数据库 运行如下命令: docker run -d --name [容器名称] -v test:/var/lib/mysql -p 3306:3306 --env MARIADB_USER=[用户名] --env MARIADB_PASSWORD=[用户密码] --env MARIADB_ROOT_PASSWORD=[root用户密码] --env MARIADB_DATABASE=[库名] mariad

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

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

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

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

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

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

  • Python 使用元类type创建类对象常见应用详解

    本文实例讲述了Python 使用元类type创建类对象.分享给大家供大家参考,具体如下: type("123") 可以查看变量的类型;同时 type("类名",(父类),{类属性:值,类属性2:值}) 可以创建一个类. 在Python中不建议一个函数具有不同的功能(重载):type()具有不同的功能是为了兼容之前的版本. 类可以创建实例对象,类对象是由元类创建的. (元类创建类,类创建实例对象) type就是元类(type本质上就是一个类) demo.py(用元类t

  • python中元类用法实例

    本文实例讲述了python中元类用法,分享给大家供大家参考.具体方法分析如下: 1.元类(metaclass)是用来创建类的类 2.type(object):返回一个对象的类型,与object.__class__的值相同,type(name,bases,dict):创建一个新的type类型,name就是新class的name,值存到__name__属性中,bases是tuple类型,值会存到__bases__中,dict的值存到__dict__中 复制代码 代码如下: class X: ... 

  • Python自定义元类的实例讲解

    1.说明 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类. 2.实例 我们可以使用类属性 __metaclass__ 把一个类的创建过程,转交给其它地方. class A(object): __metaclass__ = ... # 这个类的创建转交给其他地方 pass 先定义了类 A,然后定义了一个类属性 __metaclass__,这个属性表示创建类 A 的过程,转交给其它地方处理. 类属性 __metaclass__ 可以是

  • python 使用元类type创建类

    目录 1.type动态创建类 1.1 语法格式 1.2 案例1:使用type创建类 1.3 案例2:使用type创建带有属性(方法)的类 1.4 案例3:使用type动态创建一个继承指定类的类 前言: 通常我们创建类都是使用class 类名,但是小伙伴们有没有想过,类是由谁来创建的呢,python中常说的万物皆对象,对象是由类创建的,那类本身也可以看做是对象,类可以由元类type创建 1.type动态创建类 1.1 语法格式 type(类名,由父类名称组成的元组(可以为空),包含属性的字典(名称

  • 深入了解python中元类的相关知识

    类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段,在python中也是成立的. class ObjectCreator: pass my_object = ObjectCreator() print(my_object) """ 输出结果: <__main__.ObjectCreator object at 0x037DACD0> """ 但是,python的类不止于此,类同样也是一种对象. class Ob

  • 深入理解Python的元类

    目录 什么是元类 type元类动态创建类 自定义元类 总结 什么是元类 Python中,一切皆对象,我们定义的数字.字符串.函数.列表等都是对象,对象是类(class)的是实例,而类(class)其实也是对象,是type的实例.这个type就是Python中的元类(metaclass).所谓元类就是用于创建所有类型的类,Python中的所有新式类以及Python3中的所有类都是type元类的实例.我们看下面这个例子: print(type(0)) # <class 'int'> print(t

  • Python中的元类编程入门指引

    回顾面向对象编程 让我们先用 30 秒钟来回顾一下 OOP 到底是什么.在面向对象编程语言中,可以定义 类,它们的用途是将相关的数据和行为捆绑在一起.这些类可以继承其 父类的部分或全部性质,但也可以定义自己的属性(数据)或方法(行为).在定义类的过程结束时,类通常充当用来创建 实例(有时也简单地称为 对象)的模板.同一个类的不同实例通常有不同的数据,但"外表"都是一样 - 例如, Employee 对象 bob 和 jane 都有 .salary 和 .room_number ,但两者

  • 深入解析Python中的descriptor描述器的作用及用法

    一般来说,一个描述器是一个有"绑定行为"的对象属性(object attribute),它的访问控制被描述器协议方法重写.这些方法是 __get__(), __set__(), 和 __delete__() .有这些方法的对象叫做描述器. 默认对属性的访问控制是从对象的字典里面(__dict__)中获取(get), 设置(set)和删除(delete)它.举例来说, a.x 的查找顺序是, a.__dict__['x'] , 然后 type(a).__dict__['x'] , 然后找

随机推荐