python利用元類(lèi)和描述器實(shí)現(xiàn)ORM模型的詳細(xì)步驟
ORM模型:
ORM模型對(duì)于后端開(kāi)發(fā)來(lái)說(shuō)肯定是不陌生的,包括很多后端框架比如django,現(xiàn)在都自帶這個(gè)模型了
ORM(Object Relational Mapping)對(duì)象關(guān)系映射
Python中的類(lèi)與數(shù)據(jù)庫(kù)之間的映射,對(duì)數(shù)據(jù)的操作就不用編寫(xiě)SQL語(yǔ)言了,因?yàn)槎挤庋b好了,比如你想插入一條數(shù)據(jù),你就直接創(chuàng)建一個(gè)對(duì)象即可,
類(lèi)名 ------->>>> 數(shù)據(jù)庫(kù)中的表名
屬性 ------->>>> 數(shù)據(jù)庫(kù)中的字段
對(duì)象 ------->>>> 數(shù)據(jù)庫(kù)中的一行數(shù)據(jù)
大致就是上面的映射關(guān)系
ORM實(shí)現(xiàn)步驟:
1、利用描述器實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)字段的類(lèi)型、長(zhǎng)度限制
2、實(shí)現(xiàn)模型類(lèi),也就是創(chuàng)建一張表并定義字段
3、利用元類(lèi)實(shí)現(xiàn)映射關(guān)系
元類(lèi):
1.實(shí)例對(duì)象是通過(guò)類(lèi)來(lái)創(chuàng)建,那類(lèi)又是通過(guò)什么創(chuàng)建的呢?python中所有的類(lèi)都是通過(guò)元類(lèi)來(lái)創(chuàng)建類(lèi),且這個(gè)元類(lèi)只有一個(gè)那就是type;
class Test:
pass
t = Test()
print(type(Test)) #類(lèi)是通過(guò)元類(lèi)type創(chuàng)建
print(type(t)) #實(shí)例對(duì)象是通過(guò)類(lèi)創(chuàng)建
#輸出-----------------------
<class 'type'>
<class '__main__.Test'>
2.除了通過(guò)class關(guān)鍵字創(chuàng)建類(lèi),我們也可以通過(guò)元類(lèi)type來(lái)創(chuàng)建類(lèi),type作為python的內(nèi)置函數(shù),其實(shí)他也是一個(gè)類(lèi),他有兩個(gè)作用:
1.傳入一個(gè)變量,返回這個(gè)變量的類(lèi)型;
2.創(chuàng)建類(lèi)時(shí),第一個(gè)參數(shù)傳入類(lèi)名,第二個(gè)參數(shù)傳入一個(gè)元祖,指定父類(lèi),第三個(gè)參數(shù)傳入字典設(shè)置類(lèi)的屬性或方法;
#通過(guò)元類(lèi)來(lái)創(chuàng)建類(lèi)
test1 = type("test2",(object,),{"name":"xiaoming"})
t1 = test1()
print(type(test1))
print(type(t1))
#輸出---------------
<class 'type'>
<class '__main__.test2'>
自定義元類(lèi):
1.自定義一個(gè)元類(lèi),這個(gè)元類(lèi)必須繼承type,用這個(gè)元類(lèi)創(chuàng)建類(lèi)時(shí),必須使用metaclass指定他的元類(lèi);
class MetaClass(type):
"""自定義一個(gè)元類(lèi),這個(gè)元類(lèi)必須繼承type,用這個(gè)元類(lèi)創(chuàng)建類(lèi)時(shí),必須使用metaclass指定他的元類(lèi)"""
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'> #可以看到此時(shí)這個(gè)類(lèi)創(chuàng)建是用我們自定義的元類(lèi)創(chuàng)建的,而不是type創(chuàng)建的
描述器:
1.描述器的定義,只要一個(gè)類(lèi)中實(shí)現(xiàn)了__get__、set、__delete__中的一個(gè)或幾個(gè),這個(gè)類(lèi)的實(shí)例就可以叫描述器,那描述器有神么作用呢,在我們給描述器設(shè)置屬性時(shí),一定會(huì)調(diào)用描述器里面的—set—方法,那我們?cè)谠O(shè)置屬性之前是不是可以在set方法里面,做一些手腳,比如校驗(yàn)這個(gè)屬性長(zhǎng)度,類(lèi)型等等,如果不符合要求那就拋錯(cuò),如果符合那就設(shè)置成功;
class Describer:
"""
這是一個(gè)描述器,描述器一般不直接實(shí)例化對(duì)象,而是在另一個(gè)類(lèi)里,給他設(shè)置屬性
"""
def __set__(self, instance, value):
print("設(shè)置屬性的時(shí)候會(huì)被調(diào)用")
self.value = value
def __get__(self, instance, owner):
print("獲取屬性的時(shí)候會(huì)被調(diào)用")
return self.value
def __delete__(self, instance):
print("刪除屬性的時(shí)候會(huì)被調(diào)用")
self.value = None
class Test:
name = Describer()
t = Test()
t.name = "xiaoming" #給描述器設(shè)置一個(gè)類(lèi)屬性
print(t.name) #獲取一個(gè)類(lèi)屬性
#輸出---------------
設(shè)置屬性的時(shí)候會(huì)被調(diào)用
獲取屬性的時(shí)候會(huì)被調(diào)用
xiaoming
利用描述器實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)字段的類(lèi)型、長(zhǎng)度限制
設(shè)置屬性一定會(huì)調(diào)用描述器里面的—set—方法,再設(shè)置屬性之前校驗(yàn)這個(gè)屬性長(zhǎng)度,類(lèi)型等等,如果不符合要求那就拋錯(cuò),如果符合那就設(shè)置成功;
class BaseFiled:#為什么要先定義一個(gè)父類(lèi)呢,一會(huì)我們改造元類(lèi)的時(shí)候會(huì)用到
pass
class CharFiled(BaseFiled):
"""定義一個(gè)字符串的類(lèi)型限制"""
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):
"""定義一個(gè)數(shù)值的類(lèi)型限制"""
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
實(shí)現(xiàn)模型類(lèi),也就是創(chuàng)建一張表并定義字段:
模型類(lèi),這些字段是通過(guò)描述器來(lái)進(jìn)行長(zhǎng)度和類(lèi)型校驗(yàn)的
class User():
"""這是一個(gè)模型類(lèi),相當(dāng)于定義了一個(gè)表名是User的表,表有三個(gè)字段name,age,love"""
name = CharFiled()
age = IntFiled()
love = CharFiled(length=50)
如果是這樣的話我們給這個(gè)類(lèi)實(shí)例化的時(shí)候,需要這樣傳值:
t1 = User() t1.name = "xiaoming" t1.age = 18 t1.love = "single"
如果我們需要一次性傳入三個(gè)值得話,那就需要一個(gè)__init__來(lái)接收,但是模型類(lèi)一般里面不定義init,所以我們可以給他找個(gè)父類(lèi),然后讓他繼承父類(lèi)的init,這樣我們就可以一次傳入多個(gè)值,并且參數(shù)個(gè)數(shù)也不限定,優(yōu)化如下:
class BaseMode():
def __init__(self, **kwargs):
"""
由于每一個(gè)模型類(lèi)(也就是數(shù)據(jù)庫(kù)表)的屬性個(gè)數(shù)不一致,所以我們需要定義一個(gè)父類(lèi)來(lái)進(jìn)行定義初始化的屬性
:param kwargs:
"""
for k, v in kwargs.items(): # 遍歷傳進(jìn)來(lái)的所有屬性
setattr(self, k, v) # 拿到這些屬性后對(duì)self(也就是類(lèi)本身)進(jìn)行設(shè)置屬性
class User(BaseMode):
"""第一個(gè)模型類(lèi)"""
name = CharFiled()
age = IntFiled()
love = CharFiled(length=50)
t1 = User(name= "xiaoming",age=18,love="xxx")#現(xiàn)在我們就可以一次傳入多個(gè)值,并且參數(shù)個(gè)數(shù)也不限定
class Oder(BaseMode):
"""第二個(gè)模型類(lèi)"""
id = CharFiled()
count = IntFiled()
t2 = Oder(id ="a1",count = 123)
利用元類(lèi)實(shí)現(xiàn)模型類(lèi)和數(shù)據(jù)庫(kù)映射關(guān)系:
其實(shí)就是創(chuàng)建模型類(lèi)的時(shí)候,提取模型類(lèi)的類(lèi)名作為表名,提取模型類(lèi)的屬性作為字段名,存儲(chǔ)在模型類(lèi)的類(lèi)屬性里,然后備用,代碼如下:
class MetaClass(type):
"""自定義一個(gè)元類(lèi),這個(gè)元類(lèi)必須繼承type,用這個(gè)元類(lèi)創(chuàng)建類(lèi)時(shí),必須使用meteclass指定他的元類(lèi)"""
def __new__(cls, name, bases, dic, *args, **kwargs):
if name == "BaseMode": # (User,Oder,BaserMode都指定了用這個(gè)元類(lèi))我們的目的是對(duì)模型類(lèi)(User,Oder)進(jìn)行提取操作,不對(duì)BaserMode進(jìn)行操作,所以先判斷類(lèi)名是否為BaseMode,如果是則直接使用元類(lèi)創(chuàng)建類(lèi),不需要提取
return super().__new__(cls, name, bases, dic)
else:
table_name = name.lower() # 提取表名,即模型類(lèi)的類(lèi)名,將表名變成小寫(xiě)
filed_dic = {} # 定義一個(gè)空的列表,用來(lái)裝dic中(dic中的屬性就是模型類(lèi)里面我們寫(xiě)的字段)屬于BaseFiled類(lèi)型的屬性,因?yàn)閐ic中會(huì)有其他創(chuàng)建類(lèi)時(shí)自動(dòng)生成的屬性(例如__開(kāi)頭的一些),這些屬性我們沒(méi)必要去建立映射關(guān)系,所以需要將其剔除掉
for k, v in dic.items():
if isinstance(v,BaseFiled):
filed_dic[k] = v
dic["t_name"] = table_name # 給dic新加一個(gè)t_name的屬性,將表名添加到dic中,實(shí)現(xiàn)類(lèi)名與表名的映射關(guān)系
dic["filed_dict"] = filed_dic # 給dic新加一個(gè)filed_dict的屬性,將屬于BaseFiled類(lèi)型的屬性給添加到dic中,實(shí)現(xiàn)屬性與字段的映射關(guān)系
return super().__new__(cls, name, bases,dic)
**創(chuàng)建模型類(lèi)的時(shí)候,利用給他指定元類(lèi),提取了表名和字段名,我們具體要怎么用呢?**我們可以在模型類(lèi)的父類(lèi)里面定義一個(gè)save方法,然后用這些字段寫(xiě)sql,在我們創(chuàng)建模型類(lèi)的實(shí)例后,就調(diào)用這個(gè)save方法,即可向數(shù)據(jù)庫(kù)插入一條數(shù)據(jù)了,如下:
class BaseMode(metaclass=MetaClass):
def __init__(self, **kwargs):
"""
由于每一個(gè)模型類(lèi)(也就是數(shù)據(jù)庫(kù)表)的屬性個(gè)數(shù)不一致,所以我們需要定義一個(gè)父類(lèi)來(lái)進(jìn)行定義初始化的屬性
:param kwargs:
"""
for k, v in kwargs.items(): # 遍歷傳進(jìn)來(lái)的所有屬性
setattr(self, k, v) # 拿到這些屬性后對(duì)self(也就是類(lèi)本身)進(jìn)行設(shè)置屬性
def save(self):
"""生成SQL語(yǔ)句"""
# 獲取表名
table_name = self.t_name
# 獲取所有的屬性
fileds = self.filed_dict
dic = {} # 定義一個(gè)空字典,用來(lái)裝屬性名和屬性值
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):
"""第二個(gè)模型類(lèi)"""
id = CharFiled()
count = IntFiled()
t2 = Oder(id ="a1",count = 123)#實(shí)例化模型類(lèi),并調(diào)用save方法,即可向數(shù)據(jù)庫(kù)插入數(shù)據(jù)
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')
至此我們的一個(gè)簡(jiǎn)單的ORM模型就定義好了,我們只需要理解原理即可,因?yàn)橐粋€(gè)成熟的后端框架都有現(xiàn)成的ORM模型,例如django和flask都自帶的有,最后再附上全部代碼,如果有幫到你,能點(diǎn)個(gè)贊嗎,謝謝咯~~~
class MetaClass(type):
"""自定義一個(gè)元類(lèi),這個(gè)元類(lèi)必須繼承type,用這個(gè)元類(lèi)創(chuàng)建類(lèi)時(shí),必須使用meteclass指定他的元類(lèi)"""
def __new__(cls, name, bases, dic, *args, **kwargs):
if name == "BaseMode": # (User,Oder,BaserMode都指定了用這個(gè)元類(lèi))我們的目的是對(duì)模型類(lèi)(User,Oder)進(jìn)行提取操作,不對(duì)BaserMode進(jìn)行操作,所以先判斷類(lèi)名是否為BaseMode,如果是則直接使用元類(lèi)創(chuàng)建類(lèi),不需要提取
return super().__new__(cls, name, bases, dic)
else:
table_name = name.lower() # 提取表名,即模型類(lèi)的類(lèi)名,將表名變成小寫(xiě)
filed_dic = {} # 定義一個(gè)空的列表,用來(lái)裝dic中(dic中的屬性就是模型類(lèi)里面我們寫(xiě)的字段)屬于BaseFiled類(lèi)型的屬性,因?yàn)閐ic中會(huì)有其他創(chuàng)建類(lèi)時(shí)自動(dòng)生成的屬性(例如__開(kāi)頭的一些),這些屬性我們沒(méi)必要去建立映射關(guān)系,所以需要將其剔除掉
for k, v in dic.items():
if isinstance(v,BaseFiled):
filed_dic[k] = v
dic["t_name"] = table_name # 給dic新加一個(gè)t_name的屬性,將表名添加到dic中,實(shí)現(xiàn)類(lèi)名與表名的映射關(guān)系
dic["filed_dict"] = filed_dic # 給dic新加一個(gè)filed_dict的屬性,將屬于BaseFiled類(lèi)型的屬性給添加到dic中,實(shí)現(xiàn)屬性與字段的映射關(guān)系
return super().__new__(cls, name, bases,dic)
class BaseFiled:
pass
class CharFiled(BaseFiled):
"""定義一個(gè)字符串的類(lèi)型限制"""
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):
"""定義一個(gè)數(shù)值的類(lèi)型限制"""
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):
"""
由于每一個(gè)模型類(lèi)(也就是數(shù)據(jù)庫(kù)表)的屬性個(gè)數(shù)不一致,所以我們需要定義一個(gè)父類(lèi)來(lái)進(jìn)行定義初始化的屬性
:param kwargs:
"""
for k, v in kwargs.items(): # 遍歷傳進(jìn)來(lái)的所有屬性
setattr(self, k, v) # 拿到這些屬性后對(duì)self(也就是類(lèi)本身)進(jìn)行設(shè)置屬性
def save(self):
"""生成SQL語(yǔ)句"""
# 獲取表名
table_name = self.t_name
# 獲取所有的屬性
fileds = self.filed_dict
dic = {} # 定義一個(gè)空字典,用來(lái)裝屬性名和屬性值
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):
"""第二個(gè)模型類(lèi)"""
id = CharFiled()
count = IntFiled()
t2 = Oder(id ="a1",count = 123)
t2.save()
t1 = User(name= "xiaoming",age=18,love="xxx")
t1.save()
到此這篇關(guān)于python利用元類(lèi)和描述器實(shí)現(xiàn)ORM模型的文章就介紹到這了,更多相關(guān)python ORM模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
巧用Python裝飾器 免去調(diào)用父類(lèi)構(gòu)造函數(shù)的麻煩
巧用Python裝飾器 免去調(diào)用父類(lèi)構(gòu)造函數(shù)的麻煩,需要的朋友可以參考下2012-05-05
pytest-sugar?執(zhí)行過(guò)程中顯示進(jìn)度條的腳本分享
Pytest-sugar是一款用來(lái)改善控制臺(tái)顯示的插件,增加了進(jìn)度條顯示,使得在用例執(zhí)行過(guò)程中可以看到進(jìn)度條,而且進(jìn)度條是根據(jù)用例是否通過(guò)標(biāo)注不同顏色,非常醒目,接下來(lái)通過(guò)本文給大家分享下pytest?sugar?顯示進(jìn)度條的腳本,感興趣的朋友一起看看吧2022-12-12
用uWSGI和Nginx部署Flask項(xiàng)目的方法示例
這篇文章主要介紹了用uWSGI和Nginx部署Flask項(xiàng)目的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05
詳解python函數(shù)傳參傳遞dict/list/set等類(lèi)型的問(wèn)題
這篇文章主要介紹了詳解python函數(shù)傳參傳遞dict/list/set等類(lèi)型的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
windows+vscode安裝paddleOCR運(yùn)行環(huán)境的步驟
這篇文章主要介紹了windows+vscode安裝paddleOCR運(yùn)行環(huán)境,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11

