转载

浅谈python web框架中的orm设计

看了一下廖雪峰的那个web框架,其实就是封装了web.py,请求使用异步并将 aiomysql做 为MySQL数据库提供了异步IO的驱动,前端部分则整合了jinja.其中最难的应该是orm部分了。

下面是orm一个简单的例子。

class User(Model):     __table__ = 'users'      id = StringField(primary_key=True, default=next_id, ddl='varchar(50)')     email = StringField(ddl='varchar(50)')     passwd = StringField(ddl='varchar(50)')     admin = BooleanField()     name = StringField(ddl='varchar(50)')     image = StringField(ddl='varchar(500)')     created_at = FloatField(default=time.time)

整体来看这是一个为User的简单table,集成了Model类。然后字段则是各种类型Field。

1.先来看看字段,也就是Field部分。Field部分比较简单,继承了object,然后初始化一些字段,其实没什么好说的。在字段赋值时,参数以dict格式传入,传入到哪里呢,本来type的dict是为none的,但下面这段代码用__new__改变了原来的type,而元类是创建类的类,因此,Field字段的参数,都会在ModelMetaclass类中做处理

class ModelMetaclass(type): def __new__(cls, name, bases, attrs):  if name=='Model':   return type.__new__(cls, name, bases, attrs)  tableName = attrs.get('__table__', None) or name  logging.info('found model: %s (table: %s)' % (name, tableName))  mappings = dict()  fields = []  primaryKey = None  for k, v in attrs.items():   if isinstance(v, Field):    logging.info('  found mapping: %s ==> %s' % (k, v))    mappings[k] = v    if v.primary_key:     # 找到主键:     if primaryKey:      raise StandardError('Duplicate primary key for field: %s' % k)     primaryKey = k    else:     fields.append(k)  if not primaryKey:   raise StandardError('Primary key not found.')  for k in mappings.keys():   attrs.pop(k)  escaped_fields = list(map(lambda f: '`%s`' % f, fields))  attrs['__mappings__'] = mappings # 保存属性和列的映射关系  attrs['__table__'] = tableName  attrs['__primary_key__'] = primaryKey # 主键属性名  attrs['__fields__'] = fields # 除主键外的属性名  attrs['__select__'] = 'select `%s`, %s from `%s`' % (primaryKey, ', '.join(escaped_fields), tableName)  attrs['__insert__'] = 'insert into `%s` (%s, `%s`) values (%s)' % (tableName, ', '.join(escaped_fields), primaryKey, create_args_string(len(escaped_fields) + 1))  attrs['__update__'] = 'update `%s` set %s where `%s`=?' % (tableName, ', '.join(map(lambda f: '`%s`=?' % (mappings.get(f).name or f), fields)), primaryKey)  attrs['__delete__'] = 'delete from `%s` where `%s`=?' % (tableName, primaryKey)  return type.__new__(cls, name, bases, attrs) 

而Model类相对简单,主要是为了初始化了get/set以及几个getvalue的函数,这一块根据自己需要看看就好。

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 def getValue(self, key):  return getattr(self, key, None) def getValueOrDefault(self, key):  value = getattr(self, key, None)  if value is None:   field = self.__mappings__[key]   if field.default is not None:    value = field.default() if callable(field.default) else field.default    logging.debug('using default value for %s: %s' % (key, str(value)))    setattr(self, key, value)  return value @classmethod @asyncio.coroutine def findNumber(cls, selectField, where=None, args=None):  ' find number by select and where. '  sql = ['select %s _num_ from `%s`' % (selectField, cls.__table__)]  if where:   sql.append('where')   sql.append(where)  rs = yield from select(' '.join(sql), args, 1)  if len(rs) == 0:   return None  return rs[0]['_num_']

....

@classmethod是为了将方法变成类方法,@asyncio.coroutine则是为了做异步处理。

正文到此结束
Loading...