廖雪峰Python教程中簡單ORM程式碼解讀
阿新 • • 發佈:2020-12-22
程式碼
# -*- coding: utf-8 -*- ' Simple ORM using metaclass ' class Field(object): def __init__(self, name, column_type): self.name = name self.column_type = column_type def __str__(self): return '<%s:%s>' % (self.__class__.__name__, self.name) class StringField(Field): def __init__(self, name): super(StringField, self).__init__(name, 'varchar(100)') class IntegerField(Field): def __init__(self, name): super(IntegerField, self).__init__(name, 'bigint') class ModelMetaclass(type): # 元類實現了對model類的定製, 通過User類的繼承請求中的引數, 來實現對Model這個類的定製 # 下面的User(xxx), 經過元類的定製後, 用來建立User的類增加了一個__mapping__字典和___table__屬性 def __new__(cls, name, bases, attrs): if name=='Model': # 這裡是為了排除掉對Model類的修改, 試想一下當因為建立了一個User物件然後建立了一個帶有屬性的Model類, # 然後又因為某些原因想要單獨建立一個Model類的時候, 如果沒有這一步會創造出一個帶有部分屬性的Model # 總之就是排除了在某個子類呼叫Model時候造成的修改 return type.__new__(cls, name, bases, attrs) print('Found model: %s' % name) mappings = dict() # 在這裡列印了之後會發現每個都是field print(attrs) for k, v in attrs.items(): if isinstance(v, Field): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v for k in mappings.keys(): attrs.pop(k) attrs['__mappings__'] = mappings # 儲存屬性和列的對映關係 attrs['__table__'] = name # 假設表名和類名一致 return type.__new__(cls, name, bases, attrs) class Model(dict, metaclass=ModelMetaclass): # Model是以dict為父類, 所以通過傳入的引數確定dict的內容 def __init__(self, **kw): super(Model, self).__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'Model' object has no attribute '%s'" % key) def __setattr__(self, key, value): self[key] = value def save(self): fields = [] params = [] args = [] for k, v in self.__mappings__.items(): fields.append(v.name) params.append('?') args.append(getattr(self, k, None)) sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params)) print('SQL: %s' % sql) print('ARGS: %s' % str(args)) # testing code: class User(Model): # 這裡的幾個id, name, email和password都是類變數 id = IntegerField('id') name = StringField('username') email = StringField('email') password = StringField('password') # 這裡面的鍵值對都是用來初始化User這個dict的 u = User(id=12345, name='Michael', email='[email protected]', password='my-pwd') print(u.id) u.save()
解讀
建立一個新的User步驟
- 首先在建立一個User物件的時候, 先去找了User的父類Model
- 找到了Model並且將要載入這個類的時候, 發現Model是定義了元類的, 所以接下來要去找元類
- 在元類的__new__方法中, 由於這個呼叫是從User發起的, 所以帶進來的引數都是User裡面的引數
- 在元類中如果列印了attrs會發現都是field的子類, 可能有人會有疑惑為什麼不是User括號中的字串或者是整數
- 原因是我們目前還處在定製Model類的過程中, 而User括號裡面的是用來初始化物件的引數
- 所以說用來初始化類的引數當然就是User類中的引數了
- 判斷是定製Model類還是要初始化一個Model類
- 根據傳進來的引數(User的類變數), 給定製的Model類新增一些屬性
- 建立定製好的Model類
- 建立User物件, 並且用傳進去的引數初始化這個魔改版dict