day3 - 编写ORM

7 篇文章 0 订阅

···@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()





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值