ORM文件
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
class stringfield(field): # 定义一个字符串类
#要定义字段名,类型是varchar,主键为False等着传值,默认值为空
def __init__(self,name,column_type='varchar(255)',
primary_key = False,default = None):
super().__init__(name,column_type,primary_key,default)
class integerfield(field): # 定义一个整型类
# 要定义字段名,类型是int,主键为False等着传值,默认值为空
def __init__(self,name,column_type='int',
primary_key=False,default=0):
super().__init__(name,column_type,primary_key,default)
class ModelMetaClass(type): #类对应的是表,通过元类来使创建类时带有表的各种字段
#通过写一个new方法来控制创建类时创建的属性
def __new__(cls,name,bases,attrs):
#我们写元类是为了拦截模型表的创建,而不是models这个方法类的创建
if name == 'Models':
#返回一个没加限制的new方法
return type.__new__(cls,name,bases,attrs)
table_name = attrs.get('table_name',name) # 获取表名,如果没有那就用类名
primary_key = None
mappings = {}
for k,v in attrs.items(): # k是表的字段名,v是用字段类型创建的对象
if isinstance(v,field): # 判断是不是我们自己创建的字段,如name,id等
mappings[k] = v # 将我们自己定义的字段放入一个单独的字典里
if v.primary_key: # 判断该字段是否是主键
if primary_key: # 查看主键字段被标识出来没有,如果已经有了就报错
raise TypeError('一张表有且只有一个主键')
primary_key = v.name # 如果没有,就将主键的字段名标出来
#将单个的自定义的字段属性从attrs中拿出来
for k in mappings.keys():
attrs.pop(k)
# 判断字段有没有主键
if not primary_key:
raise TypeError('没有设置主键')
# 将表的三个特性就是表名,主键,字段付给类的属性
attrs['table_name'] = table_name
attrs['primary_key'] = primary_key
attrs['mappings'] = mappings
return type.__new__(cls, name, bases, attrs)
#定义一个方法类,里面放的是通过点方法修改数据库的函数
#继承了字典的方法,可以接收各种关键字参数并将其实例化
class Models(dict,metaclass=ModelMetaClass):
def __init__(self,**kwargs):
super().__init__(**kwargs)
#表类实例化出来的是一个字典,传入的也是一个字典,字典不能直接点什么属性
#所以我们让字典在点什么的时候触发的__getattr__里返回出字典里放的属性对应的值
#通过点方法来获取字典对象的值
def __getattr__(self,item):
return self.get(item,'没有该键对值')
#通过点方法修改或增加字典对象的值
def __setattr__(self,key,value):
self[key] = value
#定义类方法
@classmethod
def select(cls,**kwargs): # **kwargs是一个字典
ms = Mysql()
#查询有两种可能一种是取所有的数据,另一种是根据条件来查询相对应的数据
#第一种相当于select * from userinfo这样
#第二种相当于select * from userinfo where id=%s这样
#所以我们通过判断kwargs有没有传值来判断是那种查询
if not kwargs:
#当kwargs没有被传参时
sql = "select * from %s"%cls.table_name
res = ms.select(sql)
else:
#当kwargs被传值(也就是要根据条件查询时)
key = list(kwargs.keys())[0] # 规定过滤条件只有一个,我们只拿一个值
value = kwargs.get(key) # 拿到key对应的值
sql = 'select * from %s where %s=?'%(cls.table_name,key) # %s在这里是为了拼接字段名,但字段值也需要拼接,所以我们先用?代替
sql = sql.replace('?','%s') # 将上面语句中的?再替换成%s
res = ms.select(sql,value) # 结果肯定是[{},{},{}],是一个列表字典
if res:
# 通过一个列表生成式将获取到的字典用**打散,然后传入类中完成实例化
return [cls(**r) for r in res] # 把数据库中的数据全部实例化成了对象
##**把字典打散成了key=value的形式,比如(name = 'jason,password='123')
def update(self):
ms = Mysql() # 生成一个数据库操作的对象
#定义一个列表存储该表的所有字段名
fields = []
#定义一个变量来存储当前数据对象的主键
pr = None
#定义一个列表用来装字段对应的值
args = []
for k,v in self.mappings.items(): # k是字段的名字,v是字段类产生的对象
#先判断谁是主键
if v.primary_key:
#通过反射拿到主键对应的值
pr = getattr(self,v.name,v.default) # 如果没有就返回default中的值
else:
#除了主键之外所有的字段取值
fields.append(v.name+'=?') # 把字段名拼成name=?,password=?这种形式
#获取除了主键字段之外所有字段的值
args.append(getattr(self,v.name,v.default))
#先用%s来代替字段名
sql = "update %s set %s where %s=%s"%(
self.table_name,','.join(fields),self.primary_key,pr)
#','.join(fields)就是把fields中的字段名拼成用,连接的字符串形式,也就是sql语句中写在set后面需要修改的字段内容
sql = sql.replace('?','%s') # 将上面最终拼接处的语句的?再替换成%s,拿到执行前最完整的sql语句
ms.execute(sql,args) # 执行sql语句,并传入%s代替的值
def save(self):
ms = Mysql() # 生产一个数据库操作对象
#存字段名
fields = []
#存字段对应的值
values = []
#存?是用来代替字段值的,与字段名的数量相同
args = []
#拿到它的字段名和字段值
for k,v in self.mappings.items():
#去掉主键字段,一般就是是id,因为id是自动递增,所以不需要手动操作
if not v.primary_key:
fields.append(v.name) #v字段对象,拿到字段名存到fields里
args.append('?') # 每存一个一个字段名存入一个用来占位字段的值的?;
values.append(getattr(self,v.name,v.default)) # 拿到字段的名对应的值
sql = 'insert into %s(%s) values(%s)'%(
self.table_name,','.join(fields),','.join(args))
#第一个%s是表名,第一个%s是字段名,第三个%s传入的是来占位对应字段名值的?
#后两个都是用,隔开的形式如(name,age),(?,?)
sql = sql.replace('?','%s')
ms.execute(sql,values)
db_pool
from DBUtils.PooledDB import PooledDB
import pymysql
# pip3 install DBUtils
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3,
# 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='youku_demo',
charset='utf8',
autocommit='True'
)
mysql模块
class Mysql(object):
def __init__(self):
#建立连接通道,配置参数
self.conn = POOL.connection()
#设置游标对象
self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
#写一个关闭的方法,关闭光标和通道
def close_db(self):
self.cursor.close() # 关闭光标
self.conn.close() # 关闭通道
#通过sql语句进行查询的方法
def select(self,sql,args=None): # 传入sql语句和参数(将参数的默认值设为None)
self.cursor.execute(sql,args) # 执行传入的sql语句
res = self.cursor.fetchall() # 接收执行后的结果
return res # 将结果返回出来
#执行sql语句的方法
def execute(self,sql,args):
try:
self.cursor.execute(sql,args) # 执行sql语句
except BaseException as e: # 异常捕获,比如sql语句的传值有问题的时候捕获下来
print(e)