2019-02-18編寫ORM

Day 3 - 編寫ORM

閱讀: 66459

有了db模塊,操作數(shù)據(jù)庫直接寫SQL就很方便。但是,我們還缺少ORM。如果有了ORM,就可以用類似這樣的語句獲取User對(duì)象:

user =User.get('123')

而不是寫SQL然后再轉(zhuǎn)換成User對(duì)象:

u = db.select_one('select*fromuserswhereid=?', '123')

user = User(**u)

所以我們開始編寫ORM模塊:transwarp.orm。

設(shè)計(jì)ORM接口

和設(shè)計(jì)db模塊類似,設(shè)計(jì)ORM也是從上層調(diào)用者角度來設(shè)計(jì)。

我們先考慮如何定義一個(gè)User對(duì)象,然后把數(shù)據(jù)庫表users和它關(guān)聯(lián)起來。

fromtranswarp.ormimportModel, StringField, IntegerFieldclassUser(Model):__table__ ='users'id = IntegerField(primary_key=True)? ? name = StringField()

注意到定義在User類中的__table__、id和name是類的屬性,不是實(shí)例的屬性。所以,在類級(jí)別上定義的屬性用來描述User對(duì)象和表的映射關(guān)系,而實(shí)例屬性必須通過__init__()方法去初始化,所以兩者互不干擾:

# 創(chuàng)建實(shí)例:user =User(id=123, name='Michael')# 存入數(shù)據(jù)庫:user.insert()

實(shí)現(xiàn)ORM模塊

有了定義,我們就可以開始實(shí)現(xiàn)ORM模塊。

首先要定義的是所有ORM映射的基類Model:

classModel(dict):__metaclass__ = ModelMetaclassdef__init__(self, **kw):super(Model, self).__init__(**kw)def__getattr__(self, key):try:returnself[key]exceptKeyError:raiseAttributeError(r"'Dict' object has no attribute '%s'"% key)def__setattr__(self, key, value):self[key] = value

Model從dict繼承,所以具備所有dict的功能,同時(shí)又實(shí)現(xiàn)了特殊方法__getattr__()和__setattr__(),所以又可以像引用普通字段那樣寫:

>>> user['id']123>>> user.id123

Model只是一個(gè)基類,如何將具體的子類如User的映射信息讀取出來呢?答案就是通過metaclass:ModelMetaclass:

classModelMetaclass(type):def__new__(cls, name, bases, attrs):mapping = ...# 讀取cls的Field字段primary_key = ...# 查找primary_key字段__table__ = cls.__talbe__# 讀取cls的__table__字段# 給cls增加一些字段:attrs['__mapping__'] = mapping? ? ? ? attrs['__primary_key__'] = __primary_key__? ? ? ? attrs['__table__'] = __table__returntype.__new__(cls, name, bases, attrs)

這樣,任何繼承自Model的類(比如User),會(huì)自動(dòng)通過ModelMetaclass掃描映射關(guān)系,并存儲(chǔ)到自身的class中。

然后,我們往Model類添加class方法,就可以讓所有子類調(diào)用class方法:

classModel(dict):...@classmethoddefget(cls, pk):d = db.select_one('select * from %s where %s=?'% (cls.__table__, cls.__primary_key__.name), pk)returncls(**d)ifdelseNone

User類就可以通過類方法實(shí)現(xiàn)主鍵查找:

user =User.get('123')

往Model類添加實(shí)例方法,就可以讓所有子類調(diào)用實(shí)例方法:

classModel(dict):...definsert(self):params = {}fork, vinself.__mappings__.iteritems():? ? ? ? ? ? params[v.name] = getattr(self, k)? ? ? ? db.insert(self.__table__, **params)returnself

這樣,就可以把一個(gè)User實(shí)例存入數(shù)據(jù)庫:

user = User(id=123, name='Michael')user.insert()

最后一步是完善ORM,對(duì)于查找,我們可以實(shí)現(xiàn)以下方法:

find_first()

find_all()

find_by()

對(duì)于count,可以實(shí)現(xiàn):

count_all()

count_by()

以及update()和delete()方法。

最后看看我們實(shí)現(xiàn)的ORM模塊一共多少行代碼?加上注釋和doctest才僅僅300多行。用Python寫一個(gè)ORM是不是很容易呢?



引用:廖雪峰

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容