Python元類編程實現(xiàn)一個簡單的ORM
概述
什么是ORM?
ORM全稱“Object Relational Mapping”,即對象-關(guān)系映射,就是把關(guān)系數(shù)據(jù)庫的一行映射為一個對象,也就是一個類對應(yīng)一個表,這樣,寫代碼更簡單,不用直接操作SQL語句。
現(xiàn)在我們就要實現(xiàn)簡易版ORM?!?/p>
效果
class Person(Model):
"""
定義類的屬性到列的映射
"""
pid = IntegerField('id')
names = StringField('username')
email = StringField('email')
password = StringField('password')
p = Person(pid=10086, names='曉明', email='10086@163.com', password='123456')
p.save()
通過執(zhí)行save()方法 動態(tài)生成sql插入語句, 是不是很神奇, 那我們現(xiàn)在開始解析原理吧
步驟
首先我們要定義一個 Field 類 它負責保存數(shù)據(jù)庫表的字段名和字段類型:
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)
在 Field 的基礎(chǔ)上,進一步定義各種類型的 Field,比如 StringField,IntegerField 等等:
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')
下一步,就是編寫最復(fù)雜的 ModelMetaclass:
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name == "Model":
return type.__new__(cls, name, bases, attrs)
mappings = dict()
print("Found class: %s" % name)
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["__table__"] = name # 表名和類名一致
attrs["__mappings__"] = mappings # 保存屬性和列的映射關(guān)系
return type.__new__(cls, name, bases, attrs)最后就是基類 Model:
class Model(metaclass=ModelMetaclass):
def __init__(self, **kwargs):
_setattr = setattr
if kwargs:
for k, v in kwargs.items():
_setattr(self, k, v)
super(Model, self).__init__()
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(k)
params.append("?")
args.append(getattr(self, k, None))
sql = "insert into %s (%s) values (%s)" % (self.__table__, ','.join(fields), ",".join(params))
print('插入語句: %s' % sql)
print('參數(shù): %s' % str(args))
def update(self):
fields = []
args = []
for k, v in self.__mappings__.items():
if getattr(self, k, None):
fields.append(k+"=?")
args.append(getattr(self, k, None))
sql = "update %s set %s" % (self.__table__, ','.join(fields))
print("更新語句: %s " % sql)
print("參數(shù): %s" % args)
def filter(self, *args):
pass
def delete(self):
pass當用戶定義一個 class Person(Model) 繼承父類時,Python解釋器會在當前類 Person 的定義中找 __metaclass__,如果沒有找到,就繼續(xù)到父類中找 __metaclass__,實在找不到就用默認 type 類。
我們在父類 Model 中定義了 __metaclass__ 的 ModelMetaclass 來創(chuàng)建 Person 類,所以 metaclass 隱式地繼承到子類。
在 ModelMetaclass 中,一共做了幾件事情:
- 排除掉對 Model 類的修改;
- 在當前類(比如 Person )中查找定義的類的所有屬性,如果找到一個 Field 屬性,就把它保存到一個 __mappings__ 的dict中,同時從類屬性中刪除該Field屬性,否則,容易造成運行時錯誤;
- 把表名保存到 __table__ 中,這里簡化為表名默認為類名。
在Model類中,就可以定義各種操作數(shù)據(jù)庫的方法,比如save(),delete(),find(),update() 等等。
我們實現(xiàn)了save(), update()方法,把一個實例保存到數(shù)據(jù)庫中。因為有表名,屬性到字段的映射和屬性值的集合,就可以構(gòu)造出INSERT語句和UPDATE語句。
編寫代碼試試:
class UserInfo(Model):
"""
定義類的屬性到列的映射
"""
uid = IntegerField('uid')
name = StringField('username')
email = StringField('email')
password = StringField('password')
class Person(Model):
"""
定義類的屬性到列的映射
"""
pid = IntegerField('id')
names = StringField('username')
email = StringField('email')
password = StringField('password')
p = Person(pid=10086, names='曉明', email='10086@163.com', password='123456')
p.save()
u2 = UserInfo(password='123456')
u2.update()輸出
Found class: UserInfo
Found mapping: uid ==> <IntegerField:uid>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found class: Person
Found mapping: pid ==> <IntegerField:id>
Found mapping: names ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
插入語句: insert into Person (pid,names,email,password) values (?,?,?,?)
參數(shù): [10086, '曉明', '10086@163.com', '123456']
更新語句: update UserInfo set password=?
參數(shù): ['123456']
結(jié)束語
就這樣一個小巧的ORM就這么完成了。是不是學(xué)到了很多呢 ?這里利用的是元編程,很多Python框架都運用了元編程達到動態(tài)操作類。
注:上述代碼列子 結(jié)合了廖雪峰的列子和少量的django ORM源碼。
完整代碼
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):
def __new__(cls, name, bases, attrs):
if name == "Model":
return type.__new__(cls, name, bases, attrs)
mappings = dict()
print("Found class: %s" % name)
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["__table__"] = name # 表名和類名一致
attrs["__mappings__"] = mappings # 保存屬性和列的映射關(guān)系
return type.__new__(cls, name, bases, attrs)
class Model(metaclass=ModelMetaclass):
def __init__(self, **kwargs):
_setattr = setattr
if kwargs:
for k, v in kwargs.items():
_setattr(self, k, v)
super(Model, self).__init__()
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(k)
params.append("?")
args.append(getattr(self, k, None))
sql = "insert into %s (%s) values (%s)" % (self.__table__, ','.join(fields), ",".join(params))
print('插入語句: %s' % sql)
print('參數(shù): %s' % str(args))
def update(self):
fields = []
args = []
for k, v in self.__mappings__.items():
if getattr(self, k, None):
fields.append(k+"=?")
args.append(getattr(self, k, None))
sql = "update %s set %s" % (self.__table__, ','.join(fields))
print("更新語句: %s " % sql)
print("參數(shù): %s" % args)
def filter(self, *args):
pass
def delete(self):
pass
class UserInfo(Model):
"""
定義類的屬性到列的映射
"""
uid = IntegerField('uid')
name = StringField('username')
email = StringField('email')
password = StringField('password')
class Person(Model):
"""
定義類的屬性到列的映射
"""
pid = IntegerField('id')
names = StringField('username')
email = StringField('email')
password = StringField('password')
p = Person(pid=10086, names='曉明', email='10086@163.com', password='123456')
p.save()
u2 = UserInfo(password='123456')
u2.update()到此這篇關(guān)于Python元類編程實現(xiàn)一個簡單的ORM的文章就介紹到這了,更多相關(guān)Python元類編程實現(xiàn)ORM內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
window7下的python2.7版本和python3.5版本的opencv-python安裝過程
這篇文章主要介紹了window7下的python2.7版本和python3.5版本的opencv-python安裝過程,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10

