orm简单配置:
''' ORM: 对象关系映射 表 ---> 类 一条条的记录 ---> 对象 一条条记录下的字段 ---> 对象.属性 字段类型: varchar: 字段名 字段类型 是否为主键 默认值 从底层的字段类型开始编写 然后创建类对象 ''' from mysql_control import Mysql # 创建字段类 # 字段类型都需要定义一些共有的(字段名、字段类型、是否为主键、默认值)属性,所以抽象一下,先定义一个父类 class Field: 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 # 这里我们只使用int和varchar类型的字段类型,并继承Field这个父类 class IntegerField(Field): # 初始化(生成对象的时候)设置一些默认值 def __init__(self, name, column_type='int', primary_key=False, default=0): super().__init__(name, column_type, primary_key, default) class StringField(Field): def __init__(self, name, column_type='varchar(255)', primary_key=False, default=None): super().__init__(name, column_type, primary_key, default) # 创建类(表)对象 # 创建类(表)对象时候需要对其进行一定的限制--->元类可以控制创建的类 # 定义自己的元类,名字随便(见名知意即可) class OrmMetaClass(type): # 创建类的时候需要指定三个参数 分别为 类名、类的父类、类的名称空间 # 创建一个类一定触发 __new__ 方法 所以对此方法进行修改 def __new__(cls, class_name, class_base, class_attr): # 因为我们是在创建具体一个类的时候进行限定,所以对这些类的抽象父类不进行限定 # 过滤出抽象父类 if class_name == 'Models': # 这里面的Models是自己创建的抽象父类名称,可以随意同步更改 return type.__new__(cls, class_name, class_base, class_attr) # 如果是抽象父类,则不进行修改直接返回 # 不是抽象父类 # 需要对表(orm 将类映射为表)的 表名、主键、字段 进行限制 # 1.如果这个表没有定义表名,那么就把类名当作表名 table_name = class_attr.get('table_name', class_name) # 使用get获取字典的值,没有值则返回默认值--->类名 # 2.限定表只有一个主键 primary_key = None # 先将主键设为空 mappings = {} # 用来存放所以的表字段(仅仅存放表字段),方便取值 for key, value in class_attr.items(): # 遍历类名称空间中的所有items ---> 键值对的形式 # 将字段类的对象添加到 mappings 中 # 判断 value 是否为字段类的对象 if isinstance(value, Field): mappings[key] = value # 仅仅将表的字段名加进去,其他的类属性不要 # 判断这个表对象的主键属性是否为True if value.primary_key: # 判断主键是否已经设定(是否已经有一个主键了)---> 设定过后全局名称空间中的 primary_key 就会被修改,不在为None if primary_key: # 不为 None 则报下面的错 raise TypeError('only one primary_key should be set in one table!') # 报错信息:一张表只能设置一个主键 # 没有设定则获取主键,同时全局变量 primary_key 也被修改了 primary_key = value.name else: pass else: pass # 删除名称空间中与 mappings 重复的属性,节省资源(额外操作,可有可无) for key in mappings.keys(): # mappings是个字典,使用key()方法可以取出所有的 key class_attr.pop(key) # 判断是否有主键 if not primary_key: raise TypeError('must have one primary_key!') # 对名称空间中的值进行修改(重新赋值) class_attr['table_name'] = table_name class_attr['primary_key'] = primary_key class_attr['mappings'] = mappings return type.__new__(cls, class_name, class_base, class_attr) # 元类创建好了,但是所有的表对象都需要写 init 方法,所以我们可以将 init 方法抽象为一个父类 # 由于我们不知道子类中有多少属性,所以可以使用 **kwargs 来接收任意关键字参数--->继承字典类型 class Models(dict, metaclass=OrmMetaClass): # 继承字典,获取字典的方法;并指定元类,限制类的生成 def __init__(self, **kwargs): # **能够将字典中的键值对打散 super().__init__(**kwargs) # 掩盖了父类的初始化方法,就需要重新调用一下 # 由于字典类没有 点属性法 查看值以及修改值,所以我们需要自己设定 # 对象.属性没有时候会触发 __getattr__ 方法 def __getattr__(self, item): return self.get(item, '没有这个key') # 自定义返回值,且可以设定返回的默认值 # 对象.属性 = 属性值 的时候触发 __setattr__ 方法 def __setattr__(self, key, value): self[key] = value # 功能函数内进行复制操作,达到点属性赋值一样的效果 # 每个类对象(表)都应具备查询数据的功能,且应为--->非绑定方法 @classmethod # 非绑定方法的关键词 def select(cls, **kwargs): # 查看的时候一般都是关键字参数条件 # 先获取数据库连接对象 ms = Mysql() # 判断**kwargs,若没有则代表没有查询条件,默认为查询所有 if not kwargs: # sql语句格式本应为 select * from 表名,我们先用占位符 %s 占位 sql = 'select * from %s' % cls.table_name # 类对象中有类名这个属性--->也就是表名 # 接收查询的结果,且返回的结果为大列表字典类型 res = ms.my_select(sql) # 因为没有条件,所以不需要进行sql语句的拼接 # 若有 kwargs,则代表查询时候有限制条件 else: # sql语句格式本应为 select * from 表名 where 条件(kwargs的字段名=kwargs的值) # 分别获取sql语句所需要的所有关键信息,并生成sql语句 # 1.获取条件的key,也就是字段名 key = list(kwargs.keys())[0] # 条件为字典,用keys()方法取值;并将结果变成有序的列表形式,依据索引取值 # 2.获取条件的value,也就是字段的值 value = kwargs.get(key) # 字典的取值方法 # 3.获取表名,也就是 cls.table_name # 生成sql语句 sql = 'select * from %s where %s = ?' % (cls.table_name, key) # 为了防止sql注入问题,关键的值不要手动拼接; # 所以先用?替换(不进行占位),然后再替换回来,再用cursor方法拼接 sql = sql.replace('?', '%s') # 替换的过程也是占位的过程 # 接收 连接对象提交sql语句执行后返回的结果;结果为 大列表套字典 类型 res = ms.my_select(sql, value) # 防止sql注入问题,能自动识别sql语句的 %s # 判断返回的结果有无,并将结果以对象的形式返回 if res: # 使用列表生成式,得到的是列表套对象 return [cls(**result) for result in res] # 将结果遍历,得到一个个字典;将字典变成对象,然后用一个大列表包裹 else: pass # 插入数据 def insert(self): # 先获取数据库连接对象 ms = Mysql() # sql语句格式本应为 insert into 表名(字段名) values(各个字段对应的值); 我们先用占位符 %s 占位 # sql = 'insert into %s(%s) values(%s)' % (...) # 表名---> cls.table_name # 字段名以及字段的值不知道 # 1.定义存放字段名、字段值以及用于存放对应字段的?号的空列表 fields = [] # 存放字段名 values = [] # 存放字段的值 args = [] # 存放对应字段的?号 # 2.获取字段名,字段值;(过滤掉主键,因为插入数据时候不需要给主键传值) for k, v in self.mappings.items(): # mappings在定义元类的时候就已经限制把表中的字段名与之存放其中了 # 过滤主键 if not v.primary_key: # 判断值这个对象的主键属性是否为 True,不是则继续 fields.append(v.name) # 将字段的名添加进去 values.append(getattr(self, v.name, v.default)) # 将字段的值添加进去,没有值则添加默认值 # v.name 是字段名 getattr(v.name) 是字段的值---> 映射 args.append('?') # 遍历循环,有多少个值就有多少个?号 # 生成sql语句 # fields与args是列表[x,x,x],但是我们的sql语句对应的形式是(x,x,x),所以进行,号拼接成目的格式 sql = 'insert into %s(%s) values(%s)' % (self.table_name, ','.join(fields), ','.join(args)) # 拼接好的sql语句中目前剩下?号这个符号,需要再次替换,以便cursor识别;同时防止sql注入问题 sql = sql.replace('?', '%s') # 使用连接对象进行sql语句的提交(插入数据没有返回值) ms.my_execute(sql, values) # 提交sql语句,并将values中的值一一对应到替换后的sql语句中 # 更新数据 def sql_update(self): # 先获取数据库连接对象 ms = Mysql() # sql语句格式本应为 update 表名 set 字段名=字段值 where 字段名=字段值; 我们先用占位符 %s 占位(这里我们默认主键当作条件的字段名) # sql = 'update %s set %s where %s=%s' % (...) # 表名---> cls.table_name # 主键名---> cls.primary_key # 字段名、字段的值以及主键的值不知道 # 1.定义存放字段名、字段值的空列表,并获取主键 fields = [] # 存放字段名 values = [] # 存放字段的值 primary_key = None # 先将主键的值设置为None,获取过后对其进行修改 # 2.获取字段名,字段值,主键;(因为更新数据时候需要用主键定位) for k, v in self.mappings.items(): # mappings在定义元类的时候就已经限制把表中的字段名与之存放其中了 # 获取主键的值 if v.primary_key: # 判断值里面主键这个属性是否为True,是就讲主键的值获取出来 primary_key = getattr(self, v.name, v.default) # 获取主键的值,没有值则添加默认值 # 获取其他字段名与值 else: # 值里面的主键属性为False,表明不是主键,也就是其他的字段 fields.append(v.name + '=?') # 对字段名进行一个拼接,因为我们要设定的值是一个键值对的形式,而我们只知道字段名 values.append(getattr(self, v.name, v.default)) # 将字段的值添加进去,没有值则添加默认值 # 生成sql语句 sql = 'update %s set %s where %s = %s' % (self.table_name, ','.join(fields), self.primary_key, primary_key) # 生成后的sql语句第二个%s对应的格式为 字段名= ? 因此再对?号进行替换,以便cursor识别,防止sql注入问题 sql = sql.replace('?', '%s') # 使用连接对象进行sql语句的提交(更新数据没有返回值) ms.my_execute(sql, values) # # 提交sql语句,并将values中的值一一对应到替换 # 至此每个实例化产生的对象都具备了以上的方法(包括点属性取值,赋值以及查询、插入、修改数据的方法) # 创建具体的对象,并继承Models,使其具备Models中的各个方法 class User(Models): # 在这里不定义表名就默认表名为类名 # table_name = 'user_info' # 每个表(类)都应具备两种数据类型---> int类型和 varchar类型 两种类型都是只有name没有默认值,所以至少传入一个name参数才能实例化出一个属性对象 id = IntegerField(name='id', primary_key=True) # 一般每个表都有个id字段,且一般将id设置为主键字段 # 注意:字段名=属性名 保持一致 # 此外,还需要一个varchar类型,我们这里以name作为另一个类属性 name = StringField(name='name') # 测试 if __name__ == '__main__': d3 = User(id=1, name='tank') print(d3) # 取值 # print(d3.get('name')) # print(d3.name) # print(d3['name']) # 赋值(修改) d3.name = 'zhao' print(d3.name)
mysql_control:
import pymysql class Mysql: # 设置单例模式 _instance = None def __new__(cls, *args, **kwargs): # 如果没有_instance就调用Mysql的父类Object中__nem__方法实例化对象,并赋值给_instance if not cls._instance: cls._instance = object.__new__(cls, *args, **kwargs) return cls._instance def __init__(self): # 建立连接 self.conn = pymysql.connect( host='127.0.0.1', port=3306, user='user', passwd='123456', db='youku', charset='utf8', autocommit=True ) # 生成游标 self.cursor = self.conn.cursor(pymysql.cursors.DictCursor) # 关闭游标、连接的方法 def close_db(self): self.cursor.close() self.conn.close() # 查看 def my_select(self, sql, args=None): # 提交查看的sql语句 self.cursor.execute(sql, args) # 接收查看结果,并返回 res = self.cursor.fetchall() return res # 返回的是字典类型数据 # 提交---> 包括插入数据,修改数据 def my_execute(self, sql, args): # 不一定能够提交成功,所以使用异常捕获 try: self.cursor.execute(sql, args) except Exception as e: print(e)