您的位置:首页 > 其它

day3 - 编写ORM

2015-06-03 16:40 344 查看

···@classmethod

一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法,而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。

既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢?
从它们的使用上来看,
@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样;

@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。

如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。

而@classmethod因为持有cls参数,可以来调用 类的属性,类的方法,实例化对象 等,避免硬编码。cls全权代理类名!

例:
class A(object):
bar = 1
def foo(self):
print 'foo'

@staticmethod
def static_foo():
print 'static_foo'
print A.bar

@classmethod
def class_foo(cls):
print 'class_foo'
print cls.bar
cls().foo()           #相当于 A().foo()

A.static_foo()
A.class_foo()


输出

static_foo

1

class_foo

1

foo

···dict的get()方法

语法:get( key [, default] )

Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.

···补充:在父类中登记所有子类的信息(如类名等) S.O.

#-*-coding:utf-8-*-

class Meta(type):
def __new__(cls, clsname, bases, dct):
res = type.__new__(cls, clsname, bases, dct)

print 'res:%s\ncls:%s\nclsname:%s\nbases:%s\ndct:%s' %(res,cls,clsname,bases,dct)

for cls in bases:

#判断bases中的类(clsname的父类)是否MetaClass创建
#Base是object的派生类,但object不是由MetaClass创建
#Extend1,Extend2继承自Base,而Base是由MetaClass创建
if isinstance(cls, Meta):
print 'cls:%s' %cls
try:
cls.extending(res)   #res就是 Base 中 extending的subclass参数,相当于执行 Base.extending(res)
except AttributeError:
pass
print 'cls:%s\nres:%s\n' %(cls,res)
return res

class Base(object):
__metaclass__ = Meta

subclasses = {}

@classmethod
def extending(cls, subclass):
cls.subclasses[subclass.__name__] = subclass    #相当于执行Base.subclasses[subclass.__name__] = subclass

class Extend1(Base):
pass

class Extend2(Base):
pass

print Base.subclasses
结果:



···dict类型的数据作为**kw关键字参数

>>> kw = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **kw)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

由此可见,dict 类型的数据是可以直接作为**kw参数传递给函数的(因为**kw本身就是 dict 类型的数据)。
当然,这样子也是可以的(用 “ = ”)

>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}


···完整代码剖析

注意一点,cls是class的简写。cls的含义在不同地方的含义是不一样的:在ModelMetaclass中出现的cls指代的 是也只能是 ModelMetaclass(对象,object——当然可以有自己的属性subclasses)——这是的cls用mcs代替就更直观了;而在Model中出现的cls指代的是Model或其他以Model为父类的派生类,在测定例中是User,当然还可以是School,Book等等,只要继承自Model就可以。以 def
get(cls, pk): 为例,测试中调用User.get(10190),cls就是User,相当于实例调用中的self,只不过用了@classmethod之后,可以用类直接调用而已。

# -*- coding: utf-8 -*-

'''
Database operation module. This module is independent with web module.
'''

import time, logging

import db

class Field(object):

_count = 0                                       #Field的类属性

def __init__(self, **kw):
self.name = kw.get('name', None)
self._default = kw.get('default', None)
self.primary_key = kw.get('primary_key', False)  #primary_key只能有一个
self.nullable = kw.get('nullable', False)        #默认不可空
self.updatable = kw.get('updatable', True)
self.insertable = kw.get('insertable', True)     #表的每一列都是默认可插入的
self.ddl = kw.get('ddl', '')
self._order = Field._count                       #记录参数(id,name,email,passwd,last_modified)的序号
Field._count = Field._count + 1

@property                                            #@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')
self.updatable and s.append('U')
self.insertable and s.append('I')
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(255)'
super(StringField, self).__init__(**kw)

class IntegerField(Field):

def __init__(self, **kw):
if not 'default' in kw:
kw['default'] = 0
if not 'ddl' in kw:
kw['ddl'] = 'bigint'
super(IntegerField, self).__init__(**kw)

class FloatField(Field):

def __init__(self, **kw):
if not 'default' in kw:
kw['default'] = 0.0
if not 'ddl' in kw:
kw['ddl'] = 'real'
super(FloatField, self).__init__(**kw)

class BooleanField(Field):

def __init__(self, **kw):
if not 'default' in kw:
kw['default'] = False
if not 'ddl' in kw:
kw['ddl'] = 'bool'
super(BooleanField, self).__init__(**kw)

class TextField(Field):

def __init__(self, **kw):
if not 'default' in kw:
kw['default'] = ''
if not 'ddl' in kw:
kw['ddl'] = 'text'
super(TextField, self).__init__(**kw)

class BlobField(Field):

def __init__(self, **kw):
if not 'default' in kw:
kw['default'] = ''
if not 'ddl' in kw:
kw['ddl'] = 'blob'
super(BlobField, self).__init__(**kw)

class VersionField(Field):

def __init__(self, name=None):
super(VersionField, self).__init__(name=name, default=0, ddl='bigint')

_triggers = frozenset(['pre_insert', 'pre_update', 'pre_delete'])

#attrs['__sql__'] = lambda self: _gen_sql(attrs['__table__'], mappings)
def _gen_sql(table_name, mappings):
pk = None
sql = ['-- generating SQL for %s:' % table_name, 'create table `%s` (' % table_name]   #sql是字符串元素组成的list
for v in sorted(mappings.values(), lambda x, y: cmp(x._order, y._order)):              #mappings.values()是个list,元素是实例(object)
if not hasattr(v, 'ddl'):
raise StandardError('no ddl in field "%s".' % n)
ddl = v.ddl
nullable = v.nullable
if v.primary_key:
pk = v.name
sql.append(nullable and '  `%s` %s,' % (v.name, ddl) or '  `%s` %s not null,' % (v.name, ddl))
sql.append('  primary key(`%s`)' % pk)
sql.append(');')
return '\n'.join(sql)

class ModelMetaclass(type):
'''
Metaclass for model objects.
'''
def __new__(cls, name, bases, attrs):
# skip base Model class:
if name=='Model':
return type.__new__(cls, name, bases, attrs)

'''
store all subclasses info: 记录所有由ModelMetaclass所建的子类的信息
如果cls即ModelMetaclass类(只有类可以设置属性)没有subclasses属性,
就创建一个,是字典类型的数据,存储调用ModelMetaclass而创建的类的类名(如User,School等类)
'''

if not hasattr(cls, 'subclasses'):
cls.subclasses = {}
if not name in cls.subclasses:
cls.subclasses[name] = name
else:
logging.warning('Redefine class: %s' % name)

logging.info('Scan ORMapping %s...' % name)
mappings = dict()
primary_key = None
for k, v in attrs.iteritems():
if isinstance(v, Field):
if not v.name:                      #v.name为空字符串,就默认用attrs的key值做name,id = IntegerField(primary_key=True)
v.name = k
logging.info('Found mapping: %s => %s' % (k, v))
# check duplicate primary key:
if v.primary_key:
if primary_key:
raise TypeError('Cannot define more than 1 primary key in class: %s' % name)
if v.updatable:
logging.warning('NOTE: change primary key to non-updatable.')
v.updatable = False
if v.nullable:                #保证pk默认不为空
logging.warning('NOTE: change primary key to non-nullable.')
v.nullable = False
primary_key = v               #名花有主
mappings[k] = v
# check exist of primary key:
if not primary_key:
raise TypeError('Primary key not defined in class: %s' % name)
for k in mappings.iterkeys():
attrs.pop(k)
if not '__table__' in attrs:              #默认在用户接口不需要写__table__类属性(则默认等于类名的小写字母)
attrs['__table__'] = name.lower()
attrs['__mappings__'] = mappings
attrs['__primary_key__'] = primary_key    #primary_key作为类属性(也是个类,有属性)
attrs['__sql__'] = lambda self: _gen_sql(attrs['__table__'], mappings)
for trigger in _triggers:                 #继续添加属性
if not trigger in attrs:
attrs[trigger] = None
return type.__new__(cls, name, bases, attrs)

class Model(dict):
'''
Base class for ORM.

>>> class User(Model):
...     id = IntegerField(primary_key=True)
...     name = StringField()
...     email = StringField(updatable=False)
...     passwd = StringField(default=lambda: '******')
...     last_modified = FloatField()
...     def pre_insert(self):
...         self.last_modified = time.time()
>>> u = User(id=10190, name='Michael', email='orm@db.org')
>>> r = u.insert()
>>> u.email
'orm@db.org'
>>> u.passwd
'******'
>>> u.last_modified > (time.time() - 2)
True
>>> f = User.get(10190)    #因为用了@classmethod,所以可以直接使用类名来调用方法(也必须用类名来调用,这意味着必须用@classmethod来写类的方法)
>>> f.name
u'Michael'
>>> f.email
u'orm@db.org'
>>> f.email = 'changed@db.org'
>>> r = f.update() # change email but email is non-updatable!
>>> len(User.find_all())
1
>>> g = User.get(10190)
>>> g.email
u'orm@db.org'
>>> r = g.delete()
>>> len(db.select('select * from user where id=10190'))
0
>>> import json
>>> print User().__sql__()
-- generating SQL for user:
create table `user` (
`id` bigint not null,
`name` varchar(255) not null,
`email` varchar(255) not null,
`passwd` varchar(255) not null,
`last_modified` real not null,
primary key(`id`)
);
'''
__metaclass__ = ModelMetaclass

def __init__(self, **kw):
super(Model, self).__init__(**kw)

def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

def __setattr__(self, key, value):
self[key] = value

@classmethod
def get(cls, pk):                        #cls参数无需传入,cls就是User,调用就是User.get(pk)
'''
Get by primary key.
'''
d = db.select_one('select * from %s where %s=?' % (cls.__table__, cls.__primary_key__.name), pk)
return cls(**d) if d else None      #cls(**d)等价于User(**d),d 是个dict

@classmethod
def find_first(cls, where, *args):      #where形如 'id=?'  , 调用如:User.find_first(id=?, 100)
'''
Find by where clause and return one result. If multiple results found,
only the first one returned. If no result found, return None.
'''
#d = db.select_one('select * from ? ?' % (cls.__table__, where), *args)也是可以的
d = db.select_one('select * from %s %s' % (cls.__table__, where), *args)
return cls(**d) if d else None      #cls(**d)等价于User(**d),d 是个dict

@classmethod
def find_all(cls, *args):
'''
Find all and return list.
'''
L = db.select('select * from `%s`' % cls.__table__)
return [cls(**d) for d in L]        #L是list,d是dict(L的元素),cls(**d)等价于User(**d)

@classmethod
def find_by(cls, where, *args):
'''
Find by where clause and return list.
'''
L = db.select('select * from `%s` %s' % (cls.__table__, where), *args)
return [cls(**d) for d in L]

@classmethod
def count_all(cls):
'''
Find by 'select count(pk) from table' and return integer.
'''
return db.select_int('select count(`%s`) from `%s`' % (cls.__primary_key__.name, cls.__table__))

@classmethod
def count_by(cls, where, *args):
'''
Find by 'select count(pk) from table where ... ' and return int.
'''
return db.select_int('select count(`%s`) from `%s` %s' % (cls.__primary_key__.name, cls.__table__, where), *args)

def update(self):
self.pre_update and self.pre_update()
L = []
args = []
for k, v in self.__mappings__.iteritems():
if v.updatable:
if hasattr(self, k):
arg = getattr(self, k)
else:
arg = v.default
setattr(self, k, arg)
L.append('`%s`=?' % k)
args.append(arg)
pk = self.__primary_key__.name         #实例没有__primary_key__属性,就使用类的:User.__primary_key__
args.append(getattr(self, pk))
db.update('update `%s` set %s where %s=?' % (self.__table__, ','.join(L), pk), *args)
return self

def delete(self):
self.pre_delete and self.pre_delete()
pk = self.__primary_key__.name
args = (getattr(self, pk), )
db.update('delete from `%s` where `%s`=?' % (self.__table__, pk), *args)
return self

'''
@property                                #@property 把方法变成属性
def default(self):
d = self._default
return d() if callable(d) else d
'''

def insert(self):
self.pre_insert and self.pre_insert()     #如果有self.pre_insert(就是 u.pre_insert ) 就执行( and 是‘与’ )!
params = {}
for k, v in self.__mappings__.iteritems():
if v.insertable:
if not hasattr(self, k):
setattr(self, k, v.default)
#default本来是类的方法,用了@property后可以直接用属性的方法调用。这里相当于self[k]=v.default

params[v.name] = getattr(self, k)   #等价于params[v.name] = self[k]
db.insert('%s' % self.__table__, **params)
return self

if __name__=='__main__':
logging.basicConfig(level=logging.DEBUG)
db.create_engine('root', '19921005', 'test')
db.update('drop table if exists user')
db.update('create table user (id int primary key, name text, email text, passwd text, last_modified real)')
import doctest
doctest.testmod()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: