Python ORM封装
import sys
import asyncio
import logging
logging.basicConfig(level=logging.INFO)
# 一次使用异步 处处使用异步
import aiomysql def log(sql,args=()):
logging.info('SQL:%s' %sql)
@asyncio.coroutine
def create_pool(loop, **kw): #这里的**kw是一个dict
logging.info(' start creating database connection pool')
global __pool
# 理解这里的yield from 是很重要的
#dict有一个get方法,如果dict中有对应的value值,则返回对应于key的value值,否则返回默认值,例如下面的host,如果dict里面没有
#'host',则返回后面的默认值,也就是'localhost'
#这里有一个关于Pool的连接,讲了一些Pool的知识点,挺不错的,点击打开链接,下面这些参数都会讲到,以及destroy__pool里面的
#wait_closed()
__pool=yield from aiomysql.create_pool(
host=kw.get('host','localhost'),
port=kw.get('port',3306),
user=kw['user'],
password=kw['password'],
db=kw['db'],
charset=kw.get('charset','utf8'),
autocommit=kw.get('autocommit',True), #默认自动提交事务,不用手动去提交事务
maxsize=kw.get('maxsize',10),
minsize=kw.get('minsize',1),
loop=loop
) @asyncio.coroutine
def destroy_pool():
global __pool
if __pool is not None :
__pool.close() #关闭进程池,The method is not a coroutine,就是说close()不是一个协程,所以不用yield from
yield from __pool.wait_closed() #但是wait_close()是一个协程,所以要用yield from,到底哪些函数是协程,上面Pool的链接中都有 # 我很好奇为啥不用commit 事务不用提交么?我觉得是因为上面再创建进程池的时候,有一个参数autocommit=kw.get('autocommit',True)
# 意思是默认会自动提交事务
@asyncio.coroutine
def select(sql, args, size=None):
log(sql,args)
global __pool
# 666 建立游标
# -*- yield from 将会调用一个子协程,并直接返回调用的结果
# yield from从连接池中返回一个连接, 这个地方已经创建了进程池并和进程池连接了,进程池的创建被封装到了create_pool(loop, **kw)
with (yield from __pool) as conn: #使用该语句的前提是已经创建了进程池,因为这句话是在函数定义里面,所以可以这样用
cur = yield from conn.cursor(aiomysql.DictCursor) #A cursor which returns results as a dictionary.
yield from cur.execute(sql.replace('?', '%s'), args)
if size:
rs = yield from cur.fetchmany(size) #一次性返回size条查询结果,结果是一个list,里面是tuple
else:
rs = yield from cur.fetchall() #一次性返回所有的查询结果
yield from cur.close() #关闭游标,不用手动关闭conn,因为是在with语句里面,会自动关闭,因为是select,所以不需要提交事务(commit)
logging.info('rows have returned %s' %len(rs))
return rs #返回列表查询结果,里面的元素是tuple # 封装INSERT, UPDATE, DELETE
# 语句操作参数一样,所以定义一个通用的执行函数,只是操作参数一样,但是语句的格式不一样
# 返回操作影响的行号
# 我想说的是 知道影响行号有个叼用 @asyncio.coroutine
def execute(sql,args, autocommit=True):
log(sql)
global __pool
with (yield from __pool) as conn:
try:
# 因为execute类型sql操作返回结果只有行号,不需要dict
cur = yield from conn.cursor()
# 顺便说一下 后面的args 别掉了 掉了是无论如何都插入不了数据的
yield from cur.execute(sql.replace('?', '%s'), args)#把所有的问号替换成占位符
yield from conn.commit() #这里为什么还要手动提交数据
affected_line=cur.rowcount
yield from cur.close()
print('execute : ', affected_line)
except BaseException as e:
raise
return affected_line # 这个函数主要是把查询字段计数 替换成sql识别的?
# 比如说:insert into `User` (`password`, `email`, `name`, `id`) values (?,?,?,?) 看到了么 后面这四个问号
def create_args_string(num):
lol=[]
for n in range(num):
lol.append('?')
return (','.join(lol)) # 定义Field类,负责保存(数据库)表的字段名和字段类型
class Field(object):
# 表的信息包含字段名字、类型、是否为表的主键和默认值
def __init__(self, name, column_type, primary__key, default):
self.name = name
self.column_type=column_type
self.primary_key=primary__key
self.default=default
def __str__(self):
# 返回 表名字 字段名 和字段类型
return "<%s , %s , %s>" %(self.__class__.__name__, self.name, self.column_type)
# 定义数据库中五个存储类型
class StringField(Field):
def __init__(self, name=None, primary_key=False, default=None, ddl='varchar(100)'):
super().__init__(name,ddl,primary_key,default)
# 布尔类型不可以作为主键
class BooleanField(Field):
def __init__(self, name=None, default=False):
super().__init__(name,'Boolean',False, default)
# 不知道这个column type是否可以自己定义 先自己定义看一下
class IntegerField(Field):
def __init__(self, name=None, primary_key=False, default=0):
super().__init__(name, 'int', primary_key, default)
class FloatField(Field):
def __init__(self, name=None, primary_key=False,default=0.0):
super().__init__(name, 'float', primary_key, default)
class TextField(Field):
def __init__(self, name=None, default=None):
super().__init__(name,'text',False, default)
# class Model(dict,metaclass=ModelMetaclass): # -*-定义Model的元类 # 所有的元类都继承自type
# ModelMetaclass元类定义了所有Model基类(继承ModelMetaclass)的子类实现的操作 # -*-ModelMetaclass的工作主要是为一个数据库表映射成一个封装的类做准备:
# ***读取具体子类(user)的映射信息
# 创造类的时候,排除对Model类的修改
# 在当前类中查找所有的类属性(attrs),如果找到Field属性,就将其保存到__mappings__的dict中,同时从类属性中删除Field(防止实例属性遮住类的同名属性)
# 将数据库表名保存到__table__中 # 完成这些工作就可以在Model中定义各种数据库的操作方法
# metaclass是类的模板,所以必须从`type`类型派生:
class ModelMetaclass(type):
# __new__控制__init__的执行,所以在其执行之前
# cls:代表要__init__的类,此参数在实例化时由Python解释器自动提供(例如下文的User和Model)
# name:类名
# bases:代表继承父类的集合
# attrs:类的方法及属性的集合
def __new__(cls, name, bases, attrs):
# 如果类名是Model,不做修改直接返回.排除model 是因为要排除对model类的修改
if name=='Model':
return type.__new__(cls, name, bases, attrs)
# 获取table名称 为啥获取table名称 至于在哪里我也是不明白握草
table_name=attrs.get('__table__', None) or name #r如果存在表名,则返回表名,否则返回 name
logging.info('found table: %s (table: %s) ' %(name,table_name ))
# 获取Field所有主键名和Field
mappings=dict()
fields=[] #field保存的是除主键外的属性名
primaryKey=None
# 这个k是表示字段名
for k, v in attrs.items():
if isinstance(v, Field):#判断v是否是Field的实例
logging.info('Found mapping %s===>%s' %(k, v))
# 注意mapping的用法
mappings[k] = v
if v.primary_key:
logging.info('fond primary key %s'%k)
# 这里很有意思 当第一次主键存在primaryKey被赋值 后来如果再出现主键的话就会引发错误
if primaryKey:
raise RuntimeError('Duplicated key for field') #一个表只能有一个主键,当再出现一个主键的时候就报错
primaryKey=k # 也就是说主键只能被设置一次
else:
fields.append(k) if not primaryKey: #如果主键不存在也将会报错,在这个表中没有找到主键,一个表只能有一个主键,而且必须有一个主键
raise RuntimeError('Primary key not found!')
# w下面位字段从类属性中删除Field 属性
for k in mappings.keys():
attrs.pop(k)
# 保存除主键外的属性为''列表形式
# 将除主键外的其他属性变成`id`, `name`这种形式,关于反引号``的用法,可以参考点击打开链接
escaped_fields=list(map(lambda f:'`%s`' %f, fields))
# 保存属性和列的映射关系
attrs['__mappings__']=mappings
# 保存表名
attrs['__table__']=table_name #这里的tablename并没有转换成反引号的形式
# 保存主键名称
attrs['__primary_key__']=primaryKey
# 保存主键外的属性名
attrs['__fields__']=fields
# 构造默认的增删改查 语句
attrs['__select__']='select `%s`, %s from `%s` '%(primaryKey,', '.join(escaped_fields), table_name)
attrs['__insert__'] = 'insert into `%s` (%s, `%s`) values (%s) ' %(table_name, ', '.join(escaped_fields), primaryKey, create_args_string(len(escaped_fields)+1))
attrs['__update__']='update `%s` set %s where `%s` = ?' % (table_name, ', '.join(map(lambda f:'`%s`=?' % (mappings.get(f).name or f), fields)), primaryKey)
attrs['__delete__']='delete from `%s` where `%s`=?' %(table_name, primaryKey)
return type.__new__(cls, name, bases, attrs) # 定义ORM所有映射的基类:Model
# Model类的任意子类可以映射一个数据库表
# Model类可以看作是对所有数据库表操作的基本定义的映射 # 基于字典查询形式
# Model从dict继承,拥有字典的所有功能,同时实现特殊方法__getattr__和__setattr__,能够实现属性操作
# 实现数据库操作的所有方法,定义为class方法,所有继承自Model都具有数据库操作方法
class Model(dict,metaclass=ModelMetaclass):
def __init__(self, **kw): #感觉这里去掉__init__的声明也是代码结果是没影响的
super(Model,self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError("'Model' object have no attribution: %s"% key)
def __setattr__(self, key, value):
self[key] =value
def getValue(self, key):
# 这个是默认内置函数实现的,如果没有key则返回None
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:
#callable方法用来检测对象是否可被调用,可被调用指的是对象能否使用()括号的方法调用, 如果不可被调用,则value=field.default
value = field.default() if callable(field.default) else field.default
logging.info('using default value for %s : %s ' % (key, str(value)))
setattr(self, key, value) return value @classmethod
# 类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。
@asyncio.coroutine
def find_all(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)
# dict 提供get方法 指定放不存在时候返回后学的东西 比如a.get('Fuck',None)
limit = kw.get('limit', None)
if limit is not None:
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)
else:
raise ValueError('Invalid limit value : %s ' % str(limit)) rs = yield from select(' '.join(sql),args) #返回的rs是一个元素是tuple的list
return [cls(**r) for r in rs] # **r 是关键字参数,构成了一个cls类的列表,其实就是每一条记录对应的类实例 @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
def find(cls, primarykey):
'''find object by primary key'''
#rs是一个list,里面是一个dict
rs = yield from select('%s where `%s`=?' %(cls.__select__, cls.__primary_key__), [primarykey], 1)
if len(rs) == 0:
return None
return cls(**rs[0]) #返回一条记录,以dict的形式返回,因为cls的夫类继承了dict类 @classmethod
@asyncio.coroutine
def findAll(cls, **kw):
rs = []
if len(kw) == 0:
rs = yield from select(cls.__select__, None)
else:
args=[]
values=[]
for k, v in kw.items():
args.append('%s=?' % k )
values.append(v)
print('%s where %s ' % (cls.__select__, ' and '.join(args)), values)#若不理解请查看join()函数
rs = yield from select('%s where %s ' % (cls.__select__, ' and '.join(args)), values)
return rs @asyncio.coroutine
def save(self):
args = list(map(self.getValueOrDefault, self.__fields__))
print('save:%s' % args)
args.append(self.getValueOrDefault(self.__primary_key__))
rows = yield from execute(self.__insert__, args)
if rows != 1:
print(self.__insert__)
logging.warning('failed to insert record: affected rows: %s' %rows) @asyncio.coroutine
def update(self): #修改数据库中已经存入的数据
args = list(map(self.getValue, self.__fields__)) #获得的value是User2实例的属性值,也就是传入的name,email,password值
args.append(self.getValue(self.__primary_key__))
rows = yield from execute(self.__update__, args)
if rows != 1:
logging.warning('failed to update record: affected rows: %s'%rows) @asyncio.coroutine
def delete(self):
args = [self.getValue(self.__primary_key__)]
rows = yield from execute(self.__delete__, args)
if rows != 1:
logging.warning('failed to delete by primary key: affected rows: %s' %rows) if __name__=="__main__":#一个类自带前后都有双下划线的方法,在子类继承该类的时候,这些方法会自动调用,比如__init__
class User2(Model): #虽然User类乍看没有参数传入,但实际上,User类继承Model类,Model类又继承dict类,所以User类的实例可以传入关键字参数
id = IntegerField('id',primary_key=True) #主键为id, tablename为User,即类名
name = StringField('name')
email = StringField('email')
password = StringField('password')
#创建异步事件的句柄
loop = asyncio.get_event_loop() #创建实例
@asyncio.coroutine
def test():
yield from create_pool(loop=loop, host='localhost', port=3306, user='root', password='Limin123?', db='test')
#user = User2(id=2, name='Tom', email='slysly759@gmail.com', password='12345')
r = yield from User2.findAll()
print(r)
#yield from user.save()
#ield from user.update()
#yield from user.delete()
# r = yield from User2.find(8)
# print(r)
# r = yield from User2.findAll()
# print(1, r)
# r = yield from User2.findAll(name='sly')
# print(2, r)
yield from destroy_pool() #关闭pool loop.run_until_complete(test())
loop.close()
if loop.is_closed():
sys.exit(0)
拆解
class Field(object):
def __init__(self,name,column_type,primary_key,default):
self.name = name
self.column_type = column_type
self.primary_key = primary_key
self.default = default
#print(self,name,column_type,primary_key,default)
def __str__(self):
return '<%s,%s,%s>' %(self.__class__.__name__,self.column_type,self.name)
class StringField(Field):
def __init__(self,name=None,primary_key=False,default=None,ddl='varchar(100)'):
super().__init__(name,ddl,primary_key,default)
#print(self,name,ddl,primary_key,default) def create_args_string(num):
lol=[]
for n in range(num):
lol.append('?')
return (','.join(lol)) # 元类,创建一个新的类,然后返回
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
#print(bases,'\n')
if name == 'Model':
return type.__new__(cls,name,bases,attrs)
tableName = attrs.get('__table__',None) or name
mappings = dict()
fields = []
primaryKey = None
#print(attrs)
for k,v in attrs.items():
if isinstance(v,Field):
mappings[k] = v
if v.primary_key:
if primaryKey:
raise RuntimeError('key aready exist')
primaryKey = k
else:
fields.append(k)
#print(fields)
print(mappings)
if not primaryKey:
raise RuntimeError('Primary key not found!')
for k in mappings.keys():
attrs.pop(k)
escaped_fields = list(map(lambda f: '`%s`' % f, fields))
print(escaped_fields,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) values (%s)' %(tableName,', '.join(escaped_fields),create_args_string(len(escaped_fields)))
attrs['__update__']='update `%s` set %s where `%s` = ?' % (tableName, ', '.join(map(lambda f:'`%s`=?' % (mappings.get(f).name or f), fields)), primaryKey) print(mappings.get('name').default)
return type.__new__(cls, name, bases, attrs) class Model(dict,metaclass=ModelMetaclass):
__metaclass__ = ModelMetaclass
def __init__(self,**kw):
super(Model,self).__init__(**kw)
#super().__init__(**kw)
def __getattr__(self,key):
try:
return self[key]
except KeyError:
raise AttributeError("'%s' instatnce has no attribute '%s'" %(self.__class__.__name__,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
setattr(self, key, value)
return value
@classmethod
def filter(cls,where='',**kwargs):
sql = 'select * from %s %s' %(cls.__table__,'where %s' %where if where else '')
print(sql)
def findAll(cls, **kw):
rs = []
if len(kw) == 0:
#rs = yield from select(cls.__select__, None)
print(kw)
else:
args=[]
values=[]
for k, v in kw.items():
#args.append('%s=?' % k )
args.append('%s=%s' % (k,v) )
values.append(v)
#print(args, values)
print('%s where %s ' % (cls.__select__, ' and '.join(args)))
print('%s where %s ' % (cls.__select__, ' and '.join(args)), values)
#rs = yield from select('%s where %s ' % (cls.__select__, ' and '.join(args)), values)
rs = '%s where %s ' % (cls.__select__, ' and '.join(args))
return rs
def save(self):
args = list(map(self.getValueOrDefault,self.__fields__))
print(self.__fields__)
print('save:%s' % args)
#args.append(self.getValueOrDefault(self.__primary_key__))
sql = self.__insert__
print(sql.replace('?','%s'),args) if __name__ == '__main__':
class User(Model):
__table__ = 'users'
__database__ = 'test3'
id = StringField('id',primary_key=True,ddl='varchar(50)')
passwd = StringField('passwd',ddl='varchar(50)',default='pwd123')
name = StringField('name',ddl='varchar(50)',default='lin') u = User(passwd='',name='linyouyi')
#u.filter(where='name="lin" passwd=123')
#u.findAll(name='lin',passwd='123')
u.save()
Python ORM封装的更多相关文章
- 将Python脚本封装成exe可执行文件 转
将Python脚本封装成exe可执行文件 http://www.cnblogs.com/renzo/archive/2012/01/01/2309260.html cx_freeze是用来将 Pyt ...
- python/ORM操作详解
一.python/ORM操作详解 ===================增==================== models.UserInfo.objects.create(title='alex ...
- python继承——封装
python继承--封装 1 为什么要封装 封装数据的主要原因是:保护隐私 封装方法的主要原因是:隔离复杂度 2 封装分为两个层面 第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空 ...
- 【转】Python基础-封装与扩展、静态方法和类方法
[转]Python基础-封装与扩展.静态方法和类方法 一.封装与扩展 封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码:而外部使用者只知道一个接口(函数),只要接口(函数 ...
- Django和SQLAlchemy,哪个Python ORM更好?
ORM是什么? 在介绍Python下的两个ORM框架(Django和SQLAlchemy)的区别之前,我们首先要充分了解ORM框架的用途. ORM代表对象关系映射.ORM中的每个单词解释了他们在实际项 ...
- python 3 封装
python 3 封装 从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小鱼,小虾,小王八,一起装进麻袋,然后把麻袋封上口子.照这种逻辑看,封装=‘隐藏’,这种理解是相当片面的. 先看如何隐藏 在 ...
- [simple-orm-mybaits]基于Mybatis的ORM封装介绍
目录 前言 ORM框架现状 Mybatis优缺点 simple-orm-mybatis设计思路介绍 simple-orm-mybatis使用说明 simple-orm-mybatis实际使用 推荐最佳 ...
- python面向对象-封装-property-接口-抽象-鸭子类型-03
封装 什么是封装: # 将复杂的丑陋的隐私的细节隐藏到内部,对外提供简单的使用接口 或 # 对外隐藏内部实现细节,并提供访问的接口 为什么需要封装 1.为了保证关键数据的安全性 2.对外部隐藏内部的实 ...
- python学习笔记:安装boost python库以及使用boost.python库封装
学习是一个累积的过程.在这个过程中,我们不仅要学习新的知识,还需要将以前学到的知识进行回顾总结. 前面讲述了Python使用ctypes直接调用动态库和使用Python的C语言API封装C函数, C+ ...
随机推荐
- python_django_上传文件
存储路径: 存储在服务器的项目的static/upfile(你说了算的文件名,但是一般俺们叫这个)文件中 配置: 配置settings.py文件 MDEIA_ROOT = os.path.join(B ...
- CF817E Choosing The Commander
\(\mathtt{CF817E}\) \(\mathcal{Description}\) 有 \(q\) 个操作\((1 \leq q \leq 10^{5})\): 1.加入一个数 \(p_i(1 ...
- set,get方法(属性,索引器)
很多时候我们不可以把一些字段暴露出来允许别人调用和修改,为了隐藏这些字段又便于加限制的使用,在面向对象编程中一般采用写get set函数的办法,比如: //字段_age, "_"表 ...
- 遍历实例化swiper
var list = $('.p04-s2 li'); list.each(function (index) { new Swiper ($(this).find('.swiper-container ...
- cookie和session 的初步介绍
Cookie和Session http协议不保存用户状态(信息) Cookie和Session都是为了能够保存用户信息 Cookie: 本质:保存在浏览器上的键值对 用途:标识当前用户信息 cooki ...
- 30分钟全方位了解阿里云Elasticsearch
摘要:阿里云Elasticsearch提供100%兼容开源Elasticsearch的功能,以及Security.Machine Learning.Graph.APM等商业功能,致力于数据分析.数据搜 ...
- PHP ftp_mdtm() 函数
ftp_mdtm() 函数返回指定文件的最后修改时间. 该函数将以 Unix 时间戳的形式返回文件的最后修改时间,如果出错则返回 -1. 语法 int ftp_mdtm ( resource $ftp ...
- Shiro学习(8)拦截器机制
8.1 拦截器介绍 Shiro使用了与Servlet一样的Filter接口进行扩展:所以如果对Filter不熟悉可以参考<Servlet3.1规范>http://www.iteye.com ...
- 深入浅出 Vue.js 第九章 解析器---学习笔记
本文结合 Vue 源码进行学习 学习时,根据 github 上 Vue 项目的 package.json 文件,可知版本为 2.6.10 解析器 一.解析器的作用 解析器的作用就是将模版解析成 AST ...
- Openfire部署(一)
1.从官网下载openfire_4_1_4.tar.gz文件: 2.上传到linux上,解压缩 3.启动openfire [root@localhost opt]# cd openfire/bin [ ...