zoukankan      html  css  js  c++  java
  • Python_Web_App_Day_2:编写ORM模块

    import asyncio
    import logging
    import aiomysql
    """
    编写orm模块
    """
    
    
    def log(sql):
        logging.info('SQL: %s' %sql)
    
    
    async def create_pool(loop, **kw):
        """
        创建一个全局的连接池,每个HTTP请求都是从池中获得数据库连接
        连接池由全局变量__pool存储,缺省的情况下将编码charset设为utf-8,自动提交事务
        """
        logging.info('creat database connecting pool....')
    
        # 全局变量
        global __pool
    
        # 创建连接所需要的参数
        __pool = await aiomysql.create_pool(
            host=kw.get('host', 'localhost'),
            port=kw.get('port', 3306),
            user=kw['user'],
            db=kw['db'],
            password=kw['password'],
            charset=kw.get('charset', 'utf8'),
            autocommit=kw.get('autocommit', True),
            maxsize=kw.get('maxsize', 10),
            minsize=kw.get('minsize', 1),
            loop=loop
        )
    
    
    def create_args_string(num):
        """
            用于输出元类中创建sql_insert语句中的占位符
        """
        L = []
        for x in range(num):
            L.append('?')
            return ','.join(L)
    
    
    async def select(sql, args, size=None):
        """
        封装select,使用Cursor对象执行select语句时,通过fetchall()可以拿到结果集。
        结果集是一个list,每个元素都是一个tuple,对应一行记录。
        """
        log(sql)
    
        # 引入全局变量
        global __pool
        with await __pool as conn:
            # 创建游标:aiomysql.DictCursor的作用使生成结果是一个dict
            cur = await conn.cursor(aiomysql.DictCursor)
    
            # 执行sql语句,sql语句的占位符是'?',而Mysql的占位符是'%s'
            await cur.execute(sql.replace('?', "%s"), args or ())
            if size:
                rs = await cur.fetchmany(size)
            else:
                rs = await cur.fetchall()
            await cur.close()
            logging.info('rows returned: %s'%len(rs))
            return rs
    
    async def execute(sql, args, autocommit=True):
        """
            封装INSTERT,UPDATE,DELETE:使用Cursor对象执行insert,update,delete语句时,
            执行结果由rowcount返回影响的行数,就可以拿到执行结果。
        """
        log(sql)
        with await __pool as conn:
            if not autocommit:
                await  conn.begin()
            try:
                async with conn.cursor(aiomysql.DictCursor) as cur:
                    await cur.execute(sql.replace('?', '%s'), args)
                    affected = cur.rowcount
                if not autocommit:
                    await  conn.begin()
            except BaseException as e:
                if not autocommit:
                    await  conn.rollback()
                raise
            return affected
    
    class Field(object):
        def __init__(self, name, colum_type, primary_key, default):
            self.name = name
            self.colum_type = colum_type
            self.primary_key = primary_key
            self.default = default
        def __str__(self):
            return '<%s:%s:%s>' %(self.__class__.__name__, self.colum_type, self.name)
    
    
    class StringField(Field):
        def __init__(self, name=None, ddl='varchar(100)', primary_key=False, default=None):
            super(StringField, self).__init__(name, ddl, primary_key, default)
    
    class BooleanField(Field):
        def __init__(self,name=None,ddl='boolean',primary_key=False,default=None):
            super(BooleanField,self).__init__(name,ddl,primary_key,default)
    
    class IntegerField(Field):
        def __init__(self,name=None,ddl='bigint',primary_key=False,default=0):
            super(IntegerField,self).__init__(name,ddl,primary_key,default)
    
    class FloatField(Field):
        def __init__(self,name=None,ddl='real',primary_key=False,default=0.0):
            super(FloatField,self).__init__(name,ddl,primary_key,default)
    
    class TextField(Field):
        def __init__(self,name=None,ddl='Text',primary_key=False,default=None):
            super(TextField,self).__init__(name,ddl,primary_key,default)
    
    
    #创建元类metaclass
    # metaclass是类的模板,所以必须从`type`类型派生:
    class ModelMetaclass(type):
        def __new__(cls, name, bases, attrs):
            if name == 'Model': #  排除掉对Model类的修改
                return type.__new__(cls, name, bases, attrs)
            tableName = attrs.get('__table__', None) or name
            logging.info('found a model: %s (table: %s)' % (name, tableName))
            # 获取所有的Field和主键名:
            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: k'%k)
                        primarykey = k # 此列设为列表的主键
                    else:
                        fields.append(k) # 保存除主键外的属性
    
            if not primarykey:
                raise StandardError('primary key not found.')
            for k in mappings.keys():
                attrs.pop(k) #从类属性中删除Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性)
            escaped_fields = list(map(lambda f: "`%s`" % f, fields))  # 转换为sql语法
    
            # 创建供Model类使用属性
            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)#查询列的名字,也看一下在Field定义上有没有定义名字,默认None
            attrs['__delete__'] = 'delete from `%s` where `%s`=?'%(tableName,primarykey)
            return type.__new__(cls,name,bases,attrs)
    
    #基类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('"Model" object has no atttibute: %s'%key)
    
        def __setattr__(self,key,value):
            self[key] = value
    
        def getValue(self,key):
            return getattr(self,key,None)
            #直接调回内置函数,注意这里没有下划符,注意这里None的用处,是为了当user没有赋值数据时,返回None,调用于update
    
        def getValueOrDefault(self,key):
            value = getattr(self,key,None) #第三个参数None,可以在没有返回数值时,返回None,调用于save
            if not value:
                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)))
            return value
    
        @classmethod
        async def findall(cls,where=None,args=None,**kw):
            sql = [cls.__select__]
            if where:
                sql.append('where')
                sql.append(where)
            if args is None:
                args = []
            orderBy = kw.get('orderBy',None)
            if orderBy:
                sql.append('order by')
                sql.append(orderBy)
            limit = kw.get('limit',None)
            if limit:
                sql.append('limit')
                if isinstance(limit,int):
                    sql.append('?')
                    args.append(limit)
                elif isinstance(limit,tuple) and len(limit) == 2:
                    sql.append('?, ?')
                    args.extend(limit) #tuple融入list
                else:
                    raise ValueError('Invalid limit value: %s'%str(limit))
            rs = await select(' '.join(sql),args)
            return [cls(**r) for r in rs] #调试的时候尝试了一下return rs,输出结果一样
    
        @classmethod
        async def findnumber(cls,selectField,where=None,args=None):
            sql = ['select %s __num__ from `%s`'%(selectField,cls.__table__)]
            if where:
                sql.append('where')
                sql.append(where)
            rs = await select(' '.join(sql),args,1)
            if len(rs) == 0:
                return None
            return rs[0]['__num__']
    
        @classmethod
        async def find(cls,primarykey):
            sql = '%s where `%s`=?'%(cls.__select__,cls.__primary_key__)
            rs = await select(sql,[primarykey],1)
            if len(rs) == 0:
                return None
            return cls(**rs[0])
    
        async def save(self):
            args = list(map(self.getValueOrDefault, self.__fields__))
            args.append(self.getValueOrDefault(self.__primary_key__))
            rows = await execute(self.__insert__, args)
            if rows != 1:
                logging.warn('failed to insert record: affected rows: %s' % rows)
    
        async def update(self):
            args = list(map(self.getValue,self.__fields__))
            args.append(self.getValue(self.__primary_key__))
            rows = await execute(self.__update__,args)
            if rows != 1:
                logging.warn('faild to update by primary key: affected rows: %s'%rows)
    
        async def remove(self):
            args = [self.getValue(self.__primary_key__)]#这里不能使用list()-->'int' object is not iterable
            rows = await execute(self.__delete__,args)
            if rows != 1:
                logging.warn('faild to remove by primary key: affected rows: %s'%rows)

    在一个Web App中,所有数据,包括用户信息、发布的日志、评论等,都存储在数据库中。 
    由于Web框架使用了基于asyncioaiohttp,这是基于协程的异步模型。在协程中,不能调用普通的同步IO操作,因为所有用户都是由一个线程服务的,协程的执行速度必须非常快,才能处理大量用户的请求。而耗时的IO操作不能在协程中以同步的方式调用,否则,等待一个IO操作时,系统无法响应任何其他用户。 
    这就是异步编程的一个原则:一旦决定使用异步,则系统每一层都必须是异步

    ORMObject-Relational Mapping),作用是:把关系数据库的表结构映射到对象上

    这次使用的是MySQL作为数据库。并且运用aiomysqlmysql数据库提供了异步IO的驱动。建立一个web访问的ORM,使每一个web请求被连接之后都要接入数据库进行操作。

  • 相关阅读:
    451. Sort Characters By Frequency
    424. Longest Repeating Character Replacement
    68. Text Justification
    44. Wildcard Matching
    160. Intersection of Two Linked Lists
    24. Swap Nodes in Pairs
    93. 递归实现组合型枚举
    98. 分形之城
    97. 约数之和
    96. 奇怪的汉诺塔
  • 原文地址:https://www.cnblogs.com/jp-mao/p/7061631.html
Copyright © 2011-2022 走看看