python利用元类和描述器实现ORM模型的详细步骤
作者:马儿不会跑 发布时间:2023-11-13 14:54:12
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()
来源:https://blog.csdn.net/weixin_48636525/article/details/121474590
猜你喜欢
- pytho的使用和分发完全是免费的,它是一种面向对象的语言,它的。它的类模块支持多态,操作符重载和多重继承等高级概念,并且以python特有
- 用ASP生成XBM数字图片(可用来生成验证码)XBM图片是一个纯文本的文件,可以用ASP来自动生成。可以用它来使用网站登陆的验证码;我们用记
- php var_dump 函数作用是判断一个变量的类型与长度,并输出变量的数值,如果变量有值输的是变量的值并回返数据类型.来看看var_du
- 我插入Mysql5的中文一直是乱码。但是直接使用mysqlAdmin,EMS等工具插入DB就不是乱码。而且我还可以使用程序正常地读出来。原因
- 用了这么多年的CSS,现在才明白CSS的真正匹配原理,不知道你是否也跟我一样?看1个简单的CSS:DIV#divBox p span.red
- 如何用Cookie进行登录验证?很简单,看看这两个文件:login.htm请注册登录随风起舞<FORM ACTION=&qu
- 如何用ASP发送带附件的邮件?请问如何用CDONTS组件发送带附件的邮件? 见下列代码:<%&nb
- 一般开发,SQL Server的数据库所有者为dbo.但是为了安全,有时候可能把它换成其它的名称,所有者变换不是很方便.这里列出两种供参考
- 静态方法不需要所在类被实例化就可以直接使用。静态方法效率上要比实例化高,静态方法的缺点是不自动进行销毁,而实例化的则可以做销毁。静态方法和静
- 第一列按照goodsid局部分组,然后在分组后的记录中按照audittime升序排序得到序号,从而显示某商品得第几次变迁。 第二列是取该商品
- 官方说明链接:https://intellij-support.jetbrains.com/hc/en-us/community/posts
- python 不能写new_loss=old_loss=[]这样 两个变量实际上是同一个list要分开写new_loss=[]Old_los
- 软件环境: 1、操作系统:Windows 2000 Server 2、数 据 库:Oracle 8i R2 (8.1.7) for NT 企
- python这样注释,让你的代码看起来更加的优雅,是不是常常感觉自己的python代码写出来,看起来特别的乱,虽然可以正常运行,但是在优雅性
- 二、XML的定义 XML是一个精简的SGML,它将SGML的丰富功能与HTML的易用性结合到Web的应用中。XML保留了SGML的可扩展功能
- css实现的圆角矩形的方式很多,但要追求灵活型,上面的结构简单,看起来爽一点注意css所用的图片路径,已修改兼容ie6 ie7 ff ,IE
- 本文实例为大家分享了python使用Matplotlib画条形图的具体代码,供大家参考,具体内容如下数据中国的四个直辖市分别为北京市、上海市
- 核心代码# -*- coding: utf-8 -*-'''python读取英文文件,将每个单词按照空格分开,并将每
- 作为一名数据库管理员,在进行代码迁移之前,我总是尽力给提交于开发环境的代码一个完整的面貌。但是,不得不承认,我不能保证不发生任何可能破坏开发
- 本文主要介绍如何用Python设计一个经典小游戏:猜大小。在这个游戏中,将用到前面我介绍过的所有内容:变量的使用、参数传递、函数设计、条件控