日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

利用python实现ORM

發布時間:2024/4/15 python 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用python实现ORM 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自:利用python實現ORM

做web開發的基本繞不過ORM,很多時候ORM可以大大減少開發工作量,那么ORM是怎樣實現的呢?其實很簡單,說穿了一文不值,本文先實現一個簡單的ORM然后分析下現在流行的python ORM框架peewee源碼。

ORM原理

ORM即對象關系映射(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),簡單來說就是把數據庫的一個表映射成程序中的一個對象,表中的每個字段對應程序對象的一個屬性,一個表記錄對應一個對象實例。

這樣的好處在于,數據庫的操作變得透明,底層數據庫被隔離,而且操作數據的過程中感覺不到數據庫的操作,始終處理的只有程序對象,程序結構性非常好。

如下圖,要建立一個ORM映射包括如下:

1.類名ClassName到表名TableName的映射,通常兩者相同

2.類屬性先建立到對應表字段的映射,通常屬性和字段名相同,表字段類型在類屬性初始化時指定

ORM簡單實現

按照上述,我們希望定義一個ORM類如下即可完成全部工作:

class UserModel(BaseModel):id = Field("bigint")name = Field("varchar(100)")age = Field("int")

在這個類定義中,我們指定了類屬性到表字段的映射關系,這里認為類屬性名和表字段名相同。現在類只有映射關系,還沒有具體的屬性,而且映射也不是我們想要的map形式,因此程序如下:

在BaseModel中繼承內置字典dict來完成類屬性的設置,如下

class BaseModel(dict):__metaclass__ = ModelMetaClassdef __init__(self, **kv):super(BaseModel, self).__init__(**kv)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError("Model has not key %s" % key)def __setattr__(self, key, value):self[key] = value

有了類屬性,需要將映射關系轉成我們想要的map形式,具體映射關系在子類中指定,參考上一節,借助元類在BaseModel中我們通過指定元類hook類創建過程,如下

class Field(object):def __init__(self, column_type):self.column_type = column_typeclass ModelMetaClass(type):def __new__(cls, name, bases, attr_dict):if name == 'BaseModel':return type.__new__(cls, name, bases, attr_dict)print("Creating Model:%s" % name)print(attr_dict)mapping = dict()for k,v in attr_dict.items():if isinstance(v, Field):print("Found Mapping:%s=>%s" % (k, v))setattr(v, 'name', k)mapping[k] = vattr_dict.pop(k)attr_dict['__mapping__'] = mappingattr_dict['__table__'] = namereturn type.__new__(cls, name, bases, attr_dict)

在類創建時,凡是指定Field類型的類屬性都是我們定義的映射關系,在此取出來填充Field屬性表示字段信息,并以屬性名作為key來索引對應信息,對應的表名也映射為類的名稱。

到此整個映射關系建立完成,此時實現ORM就很簡單了,基本上就是查表生成sql語句即可,如下

save保存一個記錄,按照映射關系插入對應字段對應值即可,可在BaseModel中如下實現:

def save(self):fields = []params = []args = []for k, v in self.__mapping__.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))

getone獲取指定id的一條記錄,查詢得結果,按照映射關系指定到對應屬性上即可,可在BaseModel中如下實現:

@classmethoddef getone(cls, id):# 簡化,根據數據庫查詢生成對應數據表data = {"id":"2", "name":"wenwei", "age":"19"}a = cls()for k, v in a.__mapping__.items():a.__setattr__(k, data[v.name])return a

如下調用

if __name__ == '__main__':u1 = UserModel(id='1', name='wenzhou', age='20')u1.save()u2 = UserModel.getone('2')print(u2, type(u2))

結果如下,即模擬了常見ORM的操作

Creating Model:UserModel {'age': <__main__.Field object at 0x00000000031FF710>, '__module__': '__main__', 'id': <__main__.Field object at 0x00000000031FF668>, 'name': <__main__.Field object at 0x00000000031FF6D8>} Found Mapping:age=><__main__.Field object at 0x00000000031FF710> Found Mapping:id=><__main__.Field object at 0x00000000031FF668> Found Mapping:name=><__main__.Field object at 0x00000000031FF6D8> SQL:insert into UserModel (age,id,name) values (?,?,?) ARGS:['20', '1', 'wenzhou'] ({'age': '19', 'id': '2', 'name': 'wenwei'}, <class '__main__.UserModel'>)

peewee源碼分析

python下常見的ORM有django orm、SQLAlchemy和peewee,前兩者太重,peewee相對來說比較輕量靈活,代碼非常簡潔,我們以此為例來分析下它的實現。

先看下簡單的使用,如下:

from peewee import *settings = {'host': 'localhost', 'password': '', 'port': 3306, 'user': 'root'} db = MySQLDatabase('test', **settings)class Person(Model):id = BigIntegerField()name = CharField(50)age = IntegerField()class Meta:database = dbdb_table = 'user_test'if __name__ == '__main__':db.connect()usernames = ['Bob', 'huey', 'mickey']for person in Person.select().where(Person.id >= 5):print(person.id, person.name, person.age)db.close()

這里使用和我們自定義類類似,定義一個包含表字段的Model子類,然后創建連接使用即可。它這里把連接信息database和db_table當做類的元數據Meta傳入。框架對數據庫層操作做了封裝,我們指定Person.select().where(Person.id >= 5)查詢的時候,類似我們自定義操作,根據定義的字段信息拼接出對應的SQL語句執行查詢,并填充對應的對象屬性值。

因此我們主要看下,它的字段映射關系如何指定的,一般編輯器中Ctrl選中Model,查看定義如下:

class Model(with_metaclass(ModelBase, Node)):def __init__(self, *args, **kwargs):...

對應元類為ModelBase,查看定義如下:

class ModelBase(type):...def __new__(cls, name, bases, attrs):...Meta = meta_options.get('model_metadata_class', Metadata)...# Construct the new class.cls = super(ModelBase, cls).__new__(cls, name, bases, attrs)cls.__data__ = cls.__rel__ = Nonecls._meta = Meta(cls, **meta_options)cls._schema = Schema(cls, **sopts)fields = []for key, value in cls.__dict__.items():if isinstance(value, Field):if value.primary_key and pk:raise ValueError('over-determined primary key %s.' % name)elif value.primary_key:pk, pk_name = value, keyelse:fields.append((key, value))...if pk is not False:cls._meta.set_primary_key(pk_name, pk)for name, field in fields:cls._meta.add_field(name, field)...return cls

這里判斷字段是Field實例的屬性為數據庫表對應字段,注意看這里add_field,展開如下:

def add_field(self, field_name, field, set_attribute=True):if field_name in self.fields:self.remove_field(field_name)elif field_name in self.manytomany:self.remove_manytomany(self.manytomany[field_name])...field.bind(self.model, field_name, set_attribute)

是不是很相似,先從自身移除屬性,然后綁定屬性名到新值,這個新值是什么,繼續看bind展開:

class Field(ColumnBase):...accessor_class = FieldAccessor...def bind(self, model, name, set_attribute=True):self.model = modelself.name = nameself.column_name = self.column_name or nameif set_attribute:setattr(model, name, self.accessor_class(model, self, name))class FieldAccessor(object):def __init__(self, model, field, name):self.model = modelself.field = fieldself.name = namedef __get__(self, instance, instance_type=None):if instance is not None:return instance.__data__.get(self.name)return self.fielddef __set__(self, instance, value):instance.__data__[self.name] = valueinstance._dirty.add(self.name)

可以看到,這里設置的新值就是FiledAccesor,這是個屬性描述符。

不妨print(Person.__dict__)如下:

{'name': < peewee.FieldAccessor object at 0x000000000280B6D8 > ,'age': < peewee.FieldAccessor object at 0x0000000002839748 > ,'id': < peewee.FieldAccessor object at 0x0000000002843198 > ,'_meta': < peewee.Metadata object at 0x0000000002791828 >... }

可以看到,通過元類,每個屬性已經被換成屬性描述符,通過這種方式,統一每個字段的行為,保證Person.name返回的是對應字段的描述,通過person.name返回的是具體對象實例的值。打印一個print(Person.name.__dict__)如下:

{'column_name': 'name''primary_key': False,'name': 'name','max_length': 50,'unique': False,'index': False,'model': < Model: Person > ,... }

即為對應的數據庫表信息。

可以看到peewee整個ORM實現方式和我們的如出一轍,關鍵是利用好屬性描述符和元類來完成,前者完成每個字段的類訪問和實例訪問的行為統一,后者實現攔截類創建過程實現替換屬性建立類-數據庫表映射,把握這兩點就可以分析整個ORM框架了。

?

演示代碼下載鏈接

原創,轉載請注明來自http://blog.csdn.net/wenzhou1219?

總結

以上是生活随笔為你收集整理的利用python实现ORM的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。