Python-元類實(shí)現(xiàn)ORM

元類實(shí)現(xiàn)ORM

ORM是什么
ORM 是 python編程語(yǔ)言后端web框架 Django的核心思想,“Object Relational Mapping”源武,
即對(duì)象-關(guān)系映射,簡(jiǎn)稱ORM。

一個(gè)句話理解就是:創(chuàng)建一個(gè)實(shí)例對(duì)象笛辟,用創(chuàng)建它的類名當(dāng)做數(shù)據(jù)表名藐不,用創(chuàng)建它的類屬性對(duì)應(yīng)數(shù)據(jù)表的字段旭咽,
當(dāng)對(duì)這個(gè)實(shí)例對(duì)象操作時(shí),能夠?qū)?yīng)MySQL語(yǔ)句
說(shuō)明
所謂的ORM就是讓開發(fā)者在操作數(shù)據(jù)庫(kù)的時(shí)候窒篱,能夠像操作對(duì)象時(shí)通過(guò)xxxx.屬性=yyyy一樣簡(jiǎn)單,
這是開發(fā)ORM的初衷

只不過(guò)ORM的實(shí)現(xiàn)較為復(fù)雜,Django中已經(jīng)實(shí)現(xiàn)了很復(fù)雜的操作墙杯,我們主要理解其中的意義

通過(guò)元類簡(jiǎn)單實(shí)現(xiàn)ORM中的insert功能

定義元類
class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判斷是否需要保存
        for k, v in attrs.items():
            # 判斷是否是指定的StringField或者IntegerField的實(shí)例對(duì)象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # 刪除這些已經(jīng)在字典中存儲(chǔ)的屬性
        for k in mappings.keys():
            attrs.pop(k)

        # 將之前的uid/name/email/password以及對(duì)應(yīng)的對(duì)象引用配并、類名字
        attrs['__mappings__'] = mappings  # 保存屬性和列的映射關(guān)系
        attrs['__table__'] = name  # 假設(shè)表名和類名一致
        return type.__new__(cls, name, bases, attrs)


class User(metaclass=ModelMetaclass):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    # 當(dāng)指定元類之后,以上的類屬性將不在類中高镐,而是在__mappings__屬性指定的字典中存儲(chǔ)

    # 以上User類中有
    # __mappings__ = {
    #     "uid": ('uid', "int unsigned")
    #     "name": ('username', "varchar(30)")
    #     "email": ('email', "varchar(30)")
    #     "password": ('password', "varchar(30)")
    # }
    # __table__ = "User"

    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0])
            args.append(getattr(self, k, None))

        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
        print('SQL: %s' % sql)


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
print("-"*70)
print(u.__dict__)
u.save()

打印結(jié)果:
Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
----------------------------------------------------------------------
{'uid': 12345, 'name': 'Michael', 'email': 'test@orm.org', 'password': 'my-pwd'}
SQL: insert into User (uid,username,email,password) values (12345,Michael,test@orm.org,my-pwd)

完善對(duì)數(shù)據(jù)類型的檢測(cè)

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判斷是否需要保存
        for k, v in attrs.items():
            # 判斷是否是指定的StringField或者IntegerField的實(shí)例對(duì)象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # 刪除這些已經(jīng)在字典中存儲(chǔ)的屬性
        for k in mappings.keys():
            attrs.pop(k)

        # 將之前的uid/name/email/password以及對(duì)應(yīng)的對(duì)象引用溉旋、類名字
        attrs['__mappings__'] = mappings  # 保存屬性和列的映射關(guān)系
        attrs['__table__'] = name  # 假設(shè)表名和類名一致
        return type.__new__(cls, name, bases, attrs)


class User(metaclass=ModelMetaclass):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")
    # 當(dāng)指定元類之后,以上的類屬性將不在類中嫉髓,而是在__mappings__屬性指定的字典中存儲(chǔ)

    # 以上User類中有
    # __mappings__ = {
    #     "uid": ('uid', "int unsigned")
    #     "name": ('username', "varchar(30)")
    #     "email": ('email', "varchar(30)")
    #     "password": ('password', "varchar(30)")
    # }
    # __table__ = "User"

    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0])
            args.append(getattr(self, k, None))

        args_temp = list()
        for temp in args:
            # 判斷入如果是數(shù)字類型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        # print(args_temp) # ['12345', "'Michael'", "'test@orm.org'", "'my-pwd'"]
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        print('SQL: %s' % sql)


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
print("-"*70)
print(u.__dict__)
u.save()

打印結(jié)果:
Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
----------------------------------------------------------------------
{'uid': 12345, 'name': 'Michael', 'email': 'test@orm.org', 'password': 'my-pwd'}
['12345', "'Michael'", "'test@orm.org'", "'my-pwd'"]
SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末低滩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子岩喷,更是在濱河造成了極大的恐慌恕沫,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纱意,死亡現(xiàn)場(chǎng)離奇詭異婶溯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)偷霉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門迄委,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人类少,你說(shuō)我怎么就攤上這事叙身。” “怎么了硫狞?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵信轿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我残吩,道長(zhǎng)财忽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任泣侮,我火速辦了婚禮即彪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘活尊。我一直安慰自己隶校,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布蛹锰。 她就那樣靜靜地躺著深胳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宁仔。 梳的紋絲不亂的頭發(fā)上稠屠,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天峦睡,我揣著相機(jī)與錄音,去河邊找鬼权埠。 笑死榨了,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的攘蔽。 我是一名探鬼主播龙屉,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼满俗!你這毒婦竟也來(lái)了转捕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤唆垃,失蹤者是張志新(化名)和其女友劉穎五芝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辕万,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枢步,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渐尿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片醉途。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖砖茸,靈堂內(nèi)的尸體忽然破棺而出隘擎,到底是詐尸還是另有隱情,我是刑警寧澤凉夯,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布货葬,位于F島的核電站,受9級(jí)特大地震影響恍涂,放射性物質(zhì)發(fā)生泄漏宝惰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一再沧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尊残,春花似錦炒瘸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至慰毅,卻和暖如春隘截,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工婶芭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留东臀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓犀农,卻偏偏與公主長(zhǎng)得像惰赋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呵哨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容