本文記錄下自己使用Python實(shí)現(xiàn)一個簡易的ORM模型
使用到的知識
1、元類
2、描述器
元類
對于元類,我的理解其實(shí)也便較淺,大概是這個意思
所有的類都是使用元類來進(jìn)行創(chuàng)建的,而所有的類的父類中必然是object(針對Python3),Python中的元類只有一個(type),當(dāng)然這里不包含自定義元類
下面我們來看下類的創(chuàng)建
class Test: # 定義一個類
pass
Test1 = type("Test2",(object,),{"name":"test"}) # 定義一個類
print(type(Test))
print(type(Test1))-----------------------
class 'type'>class 'type'>
從上面可以看出創(chuàng)建類,其實(shí)是有兩種方式,一種是通過class關(guān)鍵字來定義,一種是通過type來進(jìn)行創(chuàng)建,當(dāng)然常用的是使用class來進(jìn)行創(chuàng)建了,在看最后的結(jié)果,可以看出類的類型為type。說明我們這個類就是由type來創(chuàng)建的
明白了這個之后我們再來梳理下怎么使用自定義元類來創(chuàng)建類,明白一點(diǎn),自定義元類需要繼承type
class MetaClass(type): # 定義一個元類
pass
class Test(metaclass=MetaClass): # 使用自定義元類來創(chuàng)建類
pass
print(type(Test))
--------------------------
class '__main__.MetaClass'>
很明顯可以看出Test類就是用MetaClass類創(chuàng)建出來的
描述器
從描述器的定義來說,只要一個類中實(shí)現(xiàn)了__get__、__set__、__delete__中的一個或幾個,這個類的實(shí)例就可以叫描述器
下面我們來定義一個簡易的描述器
class Describer:
def __set__(self, instance, value):
print("設(shè)置屬性的時候會被調(diào)用")
self.value = value
def __get__(self, instance, owner):
print("獲取屬性的時候會被調(diào)用")
return self.value
def __delete__(self, instance):
print("刪除屬性的時候會被調(diào)用")
self.value = None
class Test:
name = Describer()
t = Test()
t.name = "xxxxx"
print(t.name)
----------------------
設(shè)置屬性的時候會被調(diào)用
獲取屬性的時候會被調(diào)用
xxxxx
從上面的代碼中有沒有什么想法?既然__set__方法會在我們設(shè)置屬性的時候會被調(diào)用,那么我們是不是可以在設(shè)置屬性前對這個屬性做一些操作呢?
ORM模型
ORM模型到底是個啥?ORM對于后端研發(fā)來說肯定是不陌生的,包括很多后端框架現(xiàn)在都自帶這個模型了
ORM(Object Relational Mapping)對象關(guān)系映射
既然是對象關(guān)系映射,那對象是啥?我的理解為:Python中的類與數(shù)據(jù)庫之間的映射,對數(shù)據(jù)的操作就不用編寫SQL語言了,因?yàn)槎挤庋b好了,比如你想插入一條數(shù)據(jù),你就直接創(chuàng)建一個對象即可,
Python ------->>>> 數(shù)據(jù)庫
類名 ------->>>> 數(shù)據(jù)庫中的表名
對象 ------->>>> 數(shù)據(jù)庫中的一行數(shù)據(jù)
屬性 ------->>>> 數(shù)據(jù)庫中的字段
大致就是上面的映射關(guān)系
ORM實(shí)現(xiàn)步驟
1、利用描述器實(shí)現(xiàn)對數(shù)據(jù)庫字段的類型、長度限制
2、實(shí)現(xiàn)Mode類,也就是Python中的類
3、利用元類實(shí)現(xiàn)映射關(guān)系
好,我們先利用描述器來實(shí)現(xiàn)對數(shù)據(jù)字段的類型,長度限制
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):
"""定義一個數(shù)值的類型限制"""
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
上面實(shí)現(xiàn)了三種,分別是字符串、數(shù)值、布爾值的,下面在來實(shí)現(xiàn)元類以及模型類
class MyMateClass(type):
"""自定義一個元類"""
def __new__(cls, name: str, bases: tuple, dic: dict, *args, **kwargs):
"""
:param name: name為模型類的類名也就是數(shù)據(jù)庫中的表名
:param bases: bases為一個元祖類型,里面裝的是name這個類的父類
:param dic: dic為一個dict類型,裝的是name這個類中的屬性
:param args:
:param kwargs:
:return:
"""
if name == "BaseMode": # 判斷類名是否為BaseMode,如果是則直接使用元類創(chuàng)建類,不做其他任何操作
return super().__new__(cls, name, bases, dic)
else:
table_name = name.lower() # 將表名變成小寫
filed_dic = {} # 定義一個空的列表,用來裝dic中屬于BaseFiled類型的屬性,因?yàn)閐ic中會有其他創(chuàng)建類時自動生成的屬性,這些屬性我們沒必要去建立映射關(guān)系,所以需要將其剔除掉
for k, v in dic.items():
if isinstance(v, BaseFiled):
filed_dic[k] = v
dic["t_name"] = table_name # 將表名添加到dic中,實(shí)現(xiàn)類名與表名的映射關(guān)系
dic["filed_dict"] = filed_dic # 將屬于BaseFiled類型的屬性給添加到dic中,實(shí)現(xiàn)屬性與字段的映射關(guān)系
return super().__new__(cls, name, bases, dic)
class BaseMode(metaclass=MyMateClass):
def __init__(self, **kwargs):
"""
由于每一個模型類(也就是數(shù)據(jù)庫表)的屬性個數(shù)不一致,所以我們需要定義一個父類來進(jìn)行定義初始化的屬性
:param kwargs:
"""
for k, v in kwargs.items(): # 遍歷傳進(jìn)來的所有屬性
setattr(self, k, v) # 拿到這些屬性后對self(也就是類本身)進(jìn)行設(shè)置屬性
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)
以上就實(shí)現(xiàn)了一個簡單的ORM模型了,這個雖然在測試開發(fā)過程中用的很少(一般都是直接用框架中封裝好的),學(xué)習(xí)這個也是為了更好的理解原理,后面好學(xué)習(xí)flask以及Django。
下面貼一下完整的代碼吧
# -*- coding: utf-8 -*-
# @Time : 2021-05-11 10:14
# @Author : cainiao
# @File : Meat.py
# @Software: PyCharm
# @Content : 實(shí)現(xiàn)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):
"""定義一個數(shù)值的類型限制"""
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):
"""定義一個數(shù)值的類型限制"""
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為模型類的類名也就是數(shù)據(jù)庫中的表名
:param bases: bases為一個元祖類型,里面裝的是name這個類的父類
:param dic: dic為一個dict類型,裝的是name這個類中的屬性
:param args:
:param kwargs:
:return:
"""
if name == "BaseMode": # 判斷類名是否為BaseMode,如果是則直接使用元類創(chuàng)建類,不做其他任何操作
return super().__new__(cls, name, bases, dic)
else:
table_name = name.lower() # 將表名變成小寫
filed_dic = {} # 定義一個空的列表,用來裝dic中屬于BaseFiled類型的屬性,因?yàn)閐ic中會有其他創(chuàng)建類時自動生成的屬性,這些屬性我們沒必要去建立映射關(guān)系,所以需要將其剔除掉
for k, v in dic.items():
if isinstance(v, BaseFiled):
filed_dic[k] = v
dic["t_name"] = table_name # 將表名添加到dic中,實(shí)現(xiàn)類名與表名的映射關(guān)系
dic["filed_dict"] = filed_dic # 將屬于BaseFiled類型的屬性給添加到dic中,實(shí)現(xiàn)屬性與字段的映射關(guān)系
return super().__new__(cls, name, bases, dic)
class BaseMode(metaclass=MyMateClass):
def __init__(self, **kwargs):
"""
由于每一個模型類(也就是數(shù)據(jù)庫表)的屬性個數(shù)不一致,所以我們需要定義一個父類來進(jìn)行定義初始化的屬性
:param kwargs:
"""
for k, v in kwargs.items(): # 遍歷傳進(jìn)來的所有屬性
setattr(self, k, v) # 拿到這些屬性后對self(也就是類本身)進(jìn)行設(shè)置屬性
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實(shí)現(xiàn)一個簡易的ORM模型的詳細(xì)內(nèi)容,更多關(guān)于python 實(shí)現(xiàn)ORM模型的資料請關(guān)注腳本之家其它相關(guān)文章!
您可能感興趣的文章:- Python Django ORM連表正反操作技巧
- 用 Python 元類的特性實(shí)現(xiàn) ORM 框架
- python 實(shí)現(xiàn)format進(jìn)制轉(zhuǎn)換與刪除進(jìn)制前綴
- Python3+SQLAlchemy+Sqlite3實(shí)現(xiàn)ORM教程
- Python的輕量級ORM框架peewee使用教程
- Python通過format函數(shù)格式化顯示值
- python利用platform模塊獲取系統(tǒng)信息
- Python colormap庫的安裝和使用詳情
- python中format函數(shù)如何使用
- Python自定義聚合函數(shù)merge與transform區(qū)別詳解
- python orm 框架中sqlalchemy用法實(shí)例詳解
- python用sqlacodegen根據(jù)已有數(shù)據(jù)庫(表)結(jié)構(gòu)生成對應(yīng)SQLAlchemy模型