ORM

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)
原文地址:https://www.cnblogs.com/wangnanfei/p/ORM.html