欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

Python学习-编写自己的ORM(1)«

shiping1 的头像

这篇博文参考的是廖雪峰的Python教程的实战部分,传送门。推荐大家看看装饰器和使用元类这两个章节,然后在看实战部分。

这篇博文有时间了还会更新,主要是学习Python的语法,如上面提到的装饰器和元类。

起步:编写简单的ORM对象

写一个类映射某个数据表,下面是写一个User类,对应数据库中的user表:

class User(Model):
id = StringField(primary_key=True,ddl='varchar(50)')
name = StringField(ddl='varchar(50)')

 

这个表中只有两个字段,id和name,为了简便,这两个字段都为StringField类型。其中StringField类型也为定义的类,对应数据库中的varchar类型。

class Field(object):
_count = 0
def __init__(self,**kw):
self.name = kw.get('name',None)
self._default = kw.get('default',None)
self.nullable = kw.get('nullable',False)
self.primary_key = kw.get('primary_key',False)
self._order = Field._count
Field._count = Field._count + 1
print 'xxx'
self.ddl = kw.get('ddl','')
@property
def default(self):
d = self._default
return d() if callable(d) else d
def __str__(self):
s = ['<%s:%s,%s,default(%s),' % (self.__class__.__name__,self.name,self.ddl,self._default)]
self.nullable and s.append('N')
s.append('>')
return ''.join(s)
class StringField(Field):
def __init__(self,**kw):
if not 'default' in kw:
kw['default'] = ''
if not 'ddl' in kw:
kw['ddl'] = 'varchar(250)'
super(StringField,self).__init__(**kw)

 其中Field为基类,可以将数据库中的类型扩充完。然后在写一个Model类:

class Model(dict):
__metaclass__ = ModelMetaclass
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
print '%s: %s' % self,value

 Model类父类为dict,其中__metaclass__为元类。这个是整个代码的基础。元类创建一个ModelMetaclass类的实例,注意__new__(cls,name,bases,attrs)中的四个参数表示的意思就很好理解了:

class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
if name == 'Model':
return type.__new__(cls,name,bases,attrs)
print ('Found model: %s' % name)
mappings = dict()
primary_key = None
li = []
for k,v in attrs.iteritems():
if isinstance(v,Field):
if not v.name:
v.name = k
if v.primary_key:
if primary_key:
raise TypeError('Cannot define more than one primary key in class: %s' % name)
if v.nullable:
v.nullable = False
primary_key = v
mappings[k] = v
if not primary_key:
raise TypeError('Primary key not defined in class: %s' % name)
for k in mappings.iterkeys():
attrs.pop(k)
attrs['__table__'] = name.lower()
attrs['__mappings__'] = mappings
attrs['__primary_key__'] = primary_key
attrs['__sql__'] =  _gen_sql(attrs['__table__'],mappings)
print str(attrs['__sql__'])
tables.append(attrs['__sql__'])
for trigger in _triggers:
if not trigger in attrs:
attrs[trigger] = None
return type.__new__(cls,name,bases,attrs)

 最后是完整的代码,不过只实现了创建表的功能,这只是万里长征的第一步,慢慢来吧:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# orm.py
tables = []
# Field类
class Field(object):
_count = 0
def __init__(self,**kw):
print 'aaa'
self.name = kw.get('name',None)
self._default = kw.get('default',None)
self.nullable = kw.get('nullable',False)
self.primary_key = kw.get('primary_key',False)
self._order = Field._count
Field._count = Field._count + 1
print 'xxx'
self.ddl = kw.get('ddl','')
@property
def default(self):
d = self._default
return d() if callable(d) else d
def __str__(self):
s = ['<%s:%s,%s,default(%s),' % (self.__class__.__name__,self.name,self.ddl,self._default)]
self.nullable and s.append('N')
s.append('>')
return ''.join(s)
class StringField(Field):
def __init__(self,**kw):
if not 'default' in kw:
kw['default'] = ''
if not 'ddl' in kw:
kw['ddl'] = 'varchar(250)'
super(StringField,self).__init__(**kw)
_triggers = frozenset(['pre_insert','pre_update','pre_insert'])
# 生成sql语句
def _gen_sql(table_name,mappings):
pk = None
sql = ['create table `%s` (' % table_name]
for f in sorted(mappings.values(),lambda x,y:cmp(x._order, y._order)):
if not hasattr(f,'ddl'):
raise StandardError('No ddl in field `%s`' % n)
ddl = f.ddl
nullable = f.nullable
if f.primary_key:
pk = f.name
sql.append(nullable and ' `%s` %s' % (f.name,ddl) or ' `%s` %s not null' % (f.name,ddl))
if pk:
sql.append(' primary key,')
pk = None
sql.append(');')
return '\n'.join(sql)
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
if name == 'Model':
return type.__new__(cls,name,bases,attrs)
print ('Found model: %s' % name)
mappings = dict()
primary_key = None
li = []
for k,v in attrs.iteritems():
if isinstance(v,Field):
if not v.name:
v.name = k
if v.primary_key:
if primary_key:
raise TypeError('Cannot define more than one primary key in class: %s' % name)
if v.nullable:
v.nullable = False
primary_key = v
mappings[k] = v
if not primary_key:
raise TypeError('Primary key not defined in class: %s' % name)
for k in mappings.iterkeys():
attrs.pop(k)
attrs['__table__'] = name.lower()
attrs['__mappings__'] = mappings
attrs['__primary_key__'] = primary_key
attrs['__sql__'] =  _gen_sql(attrs['__table__'],mappings)
print str(attrs['__sql__'])
tables.append(attrs['__sql__'])
for trigger in _triggers:
if not trigger in attrs:
attrs[trigger] = None
return type.__new__(cls,name,bases,attrs)
class Model(dict):
__metaclass__ = ModelMetaclass
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
print '%s: %s' % self,value
# 该方法可以注释掉
def save(self):
fields = []
params = []
args = []
for k,v in self.__mappings__.iteritems():
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
print ('ARGS: %s' % str(args))
class User(Model):
id = StringField(primary_key=True,ddl='varchar(50)')
name = StringField(ddl='varchar(50)')
sql = ''.join(tables)
sql = sql.replace('`','')
print sql
import mysql.connector
config = {'user':'root','password':'z5201314','host':'127.0.0.1','port':3306,'database':'test'}
cxn = mysql.connector.connect(**config)
cursor = cxn.cursor()
cursor.execute(sql)

 运行结果:

查看数据库:

不过,这才是刚刚开始。。。

来自 http://www.haodaima.net/art/2863254

普通分类: