ORM对象关系映射
ORM
ORM对象关系映射:将对象映射成数据表中的一条条记录
类 映射为 ---> 数据表名
对象 ---> 数据记录(一条条数据 比如: "张三 18岁 男")
对象.属性 ---> 数据字段(一条条数据中的具体数据 比如: "男" 或者 "18岁" 或者 "张三")
# 演示映射关系(伪代码)
""" 比如User表中有名字,年龄,性别: User表: 名字、 年龄、 性别 apple、80、female class User: # User为表名 pass user_obj = User() # 获取记录 user_obj.name属性 # 获取user表中的name数据信息 user_obj.age属性 = 80 # 给user表添加数据 """
# 字段类型的定义,每个字段都应该有 字段名、字段类型、是否为主键、是否有默认值
# 字段类型父类 class Field: def __init__(self, name, column_type, primary_key=False, default=None): self.name = name self.column_type = column_type self.primary_key = primary_key self.default = default # varchar字段类型 class StringField(Field): def __init__(self, name, column_type="varchar(64)", primary_key=False, default=None): super().__init__(name, column_type, primary_key, default) # int字段类型 class IntegerField(Field): def __init__(self, name, column_type="int", primary_key=False, default=0): super().__init__(name, column_type, primary_key, default)
# Models类
# 问题1:每一张表类,都必须定义一个__init__方法,但是无法确定每张表的字段个数以及字段名字
# 解决1:通过继承dict字典类,让用户输入所需要添加的字段以及字段名字,解决该问题
# 问题2:继承的字典无法通过 "对象.属性" 的取值方式来取值,也无法通过 "对象.属性=属性值" 的方式来赋值
# 解决2:__getattr__方法解决了取值方式,__setattr__方法解决了赋值方式
# 1.让Models类继承dict字典类,所有子类继承Models就等于继承了dict类 class Models(dict): # 2.在 "对象.属性" 获取属性时,若 "没有该属性" 时触发 __getattr__ def __getattr__(self, item): # print(item) return self.get(item) # 2.当 "对象.属性 = 属性值" ,"添加或修改属性" 时触发 __setattr__ def __setattr__(self, key, value): self[key] = value # 假如User表中有 用户名和密码 两个字段 class User(Models): pass if __name__ == '__main__': # print(dict(username='apple', password='123')) # {'username': 'apple', 'password': '123'} # User类继承了dict类,相当于 User(username='apple', password='123') == dict(username='apple', password='123') user_obj = User(username='apple', password='123') print(user_obj) # print(res.get("username")) # 字典的取值方式一 # print(res["username"]) # 字典的取值方式二 print(user_obj.username) # 字典的取值方式三,也是我们需要实现的取值方式 ---> " user_obj.name属性 # 获取user表中的name数据信息 " print('添加属性前: ---> ', user_obj) # User类实例化出来的 user_obj 普通对象 添加属性的方式 user_obj.user_type = "admin" # 实现通过 对象.属性=属性值 添加数据 ---> " user_obj.age属性 = 80 # 给user表添加数据 " print(user_obj.user_type) print('添加属性后: ---> ', user_obj) # 验证是否将 user_type="admin" 添加到名称空间中
执行结果:
{'username': 'apple', 'password': '123'} apple 添加属性前: ---> {'username': 'apple', 'password': '123'} admin 添加属性后: ---> {'username': 'apple', 'password': '123', 'user_type': 'admin'}
ORM的作用(两点)
第一个作用:控制表类的创建,也就是使用元类控制表创建的过程
第二个作用:给表类封装 查、改(增、改、删)的方法,MySQL类的封装
一、元类控制表类的创建
字段类型的定义 + Model类 + 元类
# 创建表的三个注意点:
1.保证一张表必须要有表名字
2.保证一张表中只能有一个主键
3.将所有 "字段名"与"字段对象" 添加到一个独立的字典(mappings)中,以key(字段名):value(字段对象) 添加到类的名称空间中,方便后期使用
# 字段类型父类 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 # varchar字段类型 class StringField(Field): def __init__(self, name, column_type="varchar(64)", primary_key=False, default=None): super().__init__(name, column_type, primary_key, default) # int字段类型 class IntegerField(Field): def __init__(self, name, column_type="int", primary_key=False, default=0): super().__init__(name, column_type, primary_key, default) # 自定义OrmMetaclass元类:控制类的创建过程 class OrmMetaclass(type): # __call__:先调用__new__,只要定义类就会触发__new__ def __new__(cls, class_name, class_bases, class_dict): # class_name:类名 class_bases:基类 class_dict:类的名称空间 # print(f"类名:{class_name}") # print(f"基类:{class_bases}") # print(f"类的名称空间:{class_dict}") # 过滤掉Models类,我们限制的是用户创建表的过程(可以说是用户创建表的规范),所以在此处过滤掉Models类,因为我们不限制Models类 if class_name == 'Models': return type.__new__(cls, class_name, class_bases, class_dict) # 第一个注意点:保证表必须要有表名。 获取table表名,若自定义则获取、没有则默认使用类名 # dict.get(key, class_name) key若有返回对应的值,若没有则返回默认值class_name就是默认值 table_name = class_dict.get("table_name", class_name) # 获取类名称空间的自定义表名table_name,若没有则默认以类名为表名 print(table_name) # userinfo,若未定义表名,那么表名为类名,此处即User # 自定义一个主键值标识,后面做逻辑判断使用 # 主键值:主键名为 字段名 primary_key = None # 比如主键是id,那么id字段就是主键值的名字(primary_key = id) # 第三个注意点:定义一个字典用来存放 "字段名"与"字段对象" mappings = {} print(class_dict) # 打印类中的名称空间 """ 执行结果: {'__module__': '__main__', '__qualname__': 'User', 'table_name': 'userinfo', 'id': <__main__.IntegerField object at 0x000000000287F1C8>, 'username': <__main__.StringField object at 0x000000000287F088>, 'password': <__main__.StringField object at 0x000000000287F388>} 上面class_dict的名称空间中,我们需要的仅仅是后面的id、username、password键值对属性,前两个不需要,我们需要将其剔除,后面将剔除后的结果重新放进mappings字典中 """ # 使用for循环将类中的名称空间字典 迭代依次取出 for k, v in class_dict.items(): # k为字段名,v为字段对象 # 将类中的名称空间无需的属性进行剔除筛选 # isinstance():判断一个对象是否是另一个类的实例 if isinstance(v, Field): # id、username、password的value都是Field的实例 # print(k, v) # print(v.__dict__) # {'name': 'password', 'column_type': 'varchar(64)', 'primary_key': False, 'default': None} # 第三个注意点:将所有的 "字段名" 与 "字段对象" 添加到一个定义好的独立的字典(mappings)中 mappings[k] = v """ # 此处有一个坑: # class_attr.pop(k) # 这里当字典被迭代时,不能修改其属性 """ # 第二个注意点:保证一张表只能有一个唯一的主键 # 判断字段对象如果有主键 primary_key,则为primary_key变量赋值 if v.primary_key: # 如果建的表中有主键,执行下一步,若没有不执行下一步 # 若第二次执行到此处,primary_key有值,证明有主键,抛出异常 if primary_key: raise TypeError("一张表只能有一个主键!") primary_key = v.name # 将自定义的主键值标识修改为 字段对象中的名字(和字段名一样) # 给类的名称空间 添加table_name、primary_key、mappings属性 class_dict["table_name"] = table_name # 将表名添加到类的名称空间中 class_dict["primary_key"] = primary_key # 将主键添加到类的名称空间中 class_dict["mappings"] = mappings # 将存放 "字段名"与"字段对象"的字典添加到类的名称空间中 print(class_dict) """ 执行结果: {'__module__': '__main__', '__qualname__': 'User', 'table_name': 'userinfo', 'id': <__main__.IntegerField object at 0x000000000237F1C8>, 'username': <__main__.StringField object at 0x000000000237F088>, 'password': <__main__.StringField object at 0x000000000237F388>, 'table_name': 'User', 'primary_key': 'id', 'mappings': {'id': <__main__.IntegerField object at 0x000000000237F1C8>, 'username': <__main__.StringField object at 0x000000000237F088>, 'password': <__main__.StringField object at 0x000000000237F388>}} 上面class_dict的名称空间中,有重复的"字段名"与"字段对象",在此将class_dict中重复的"字段名"与"字段对象"过滤掉 """ # 过滤掉类名称空间中重复的字段属性 for key in mappings.keys(): # 根据mappings中的字段属性来过滤掉class_dict中的字段属性,因为mappings中的字段属性在class_dict中的字段属性都存在 class_dict.pop(key) print(class_dict) """ 执行结果: {'__module__': '__main__', '__qualname__': 'User', 'table_name': 'userinfo', 'primary_key': 'id', 'mappings': {'id': <__main__.IntegerField object at 0x000000000283F1C8>, 'username': <__main__.StringField object at 0x000000000283F088>, 'password': <__main__.StringField object at 0x000000000283F388>}} 过滤完成! """ # 若建立的表中所有字段都没有主键(规定每一张表中都需要有一个主键),抛出异常 if not primary_key: raise TypeError("表中必须要有一个主键!") return type.__new__(cls, class_name, class_bases, class_dict) # 让Models类继承dict字典类,所有子类继承Models就等于继承了dict类 class Models(dict, metaclass=OrmMetaclass): # 在 "对象.属性" 获取属性时,若 "没有该属性" 时触发 __getattr__ def __getattr__(self, item): # print(item) return self.get(item) # 当 "对象.属性 = 属性值" ,"添加或修改属性" 时触发 __setattr__ def __setattr__(self, key, value): self[key] = value # 假如User表中有 id、用户名和密码 三个字段 class User(Models): table_name = "userinfo" # 自定义一个表名,若此处不写,表名默认即为类名User # 字段类中的name属性必须与User表中类属性同名 id = IntegerField(name="id", primary_key=True) username = StringField(name="username") password = StringField(name="password")
测试代码:
(错误演示)第一种:创建字段时不添加主键primary_key
(错误演示)第二种:创建字段时添加多个主键primary_key
(正确演示):创建字段时只添加一个主键primary_key
二、MySQL类的封装
给表类封装 查、改(增、改、删)的方法
mysql_setting.py:编写代码
import pymysql # MySQL连接类 class MySQL: __instance = None @classmethod def Singleton(cls): if not cls.__instance: cls.__instance = cls() # 调用类产生对象,将对象赋值给__instance return cls.__instance # 实例化MySQL类时,获取数据库连接对象,获取游标对象 def __init__(self): self.client = pymysql.connect( host="127.0.0.1", port=3306, user="root", passwd="123", db="data", charset="utf8", autocommit=True ) # 获取游标 self.cursor = self.client.cursor(pymysql.cursors.DictCursor) # 自定义查询数据方法 def select(self, sql, args=None): # 1.先提交查询sql语句 # sql = "select * from table" # sql = "select * from table where id=%s" self.cursor.execute(sql, args) # 假如查询语句中有where条件,则需要传入参数(避免sql注入,在execute中传参) # 2.获取返回的查询结果 res = self.cursor.fetchall() for data in res: print(data) # return res # 自定义提交修改数据方法(添加、更新、删除) def execute(self, sql, args=None): # 提交sql语句 # sql = "insert into table(id, username) values(%s, %s)" try: self.cursor.execute(sql, args) # 此处一定会传入参数 except Exception as e: print(e) # 关闭游标、关闭连接 def close(self): self.cursor.close() self.client.close()
mysql_test.py:测试代码
from orm.orm_demo.mysql_setting import MySQL # 导入封装好的MySQL模块 if __name__ == '__main__': # 查询数据 def select_data(sql, args=None): mysql_obj = MySQL() # sql = 'select * from userinfo where id=%s' mysql_obj.select(sql, args) print(id(mysql_obj.Singleton())) # 插入数据 def execute_data(sql, args=None): mysql_obj = MySQL() mysql_obj.execute(sql, args) print(id(mysql_obj.Singleton())) # 调用查询数据:查询插入数据前的数据 select_data("select * from userinfo") """ 执行结果: {'id': 1, 'username': 'tom', 'password': '123456', 'gender': '三年四班', 'sex': 'male', 'phone_number': 13511111111} {'id': 2, 'username': 'jack', 'password': '456789', 'gender': '三年一班', 'sex': 'male', 'phone_number': 13622222222} {'id': 3, 'username': 'jerry', 'password': '147258', 'gender': '三年二班', 'sex': 'male', 'phone_number': 13733333333} 1990595350992 """ # 插入数据 execute_data("insert into userinfo(username, password, gender, sex, phone_number) values(%s, %s, %s, %s, %s)", ("owen", "321456", "三年三班", "female", "15244444444")) """ 执行结果: 1990595350992 """ # 调用查询数据:查询插入数据后的数据 select_data("select * from userinfo") """ 执行结果: {'id': 1, 'username': 'tom', 'password': '123456', 'gender': '三年四班', 'sex': 'male', 'phone_number': 13511111111} {'id': 2, 'username': 'jack', 'password': '456789', 'gender': '三年一班', 'sex': 'male', 'phone_number': 13622222222} {'id': 3, 'username': 'jerry', 'password': '147258', 'gender': '三年二班', 'sex': 'male', 'phone_number': 13733333333} {'id': 4, 'username': 'owen', 'password': '321456', 'gender': '三年三班', 'sex': 'female', 'phone_number': 15244444444} 1990595350992 """ # 更新数据 execute_data("update userinfo set username=%s where username=%s", ("lucy", "owen")) """ 执行结果: 1990595350992 """ # 调用查询数据:查询更新数据后的数据 select_data("select * from userinfo") """ 执行结果: {'id': 1, 'username': 'tom', 'password': '123456', 'gender': '三年四班', 'sex': 'male', 'phone_number': 13511111111} {'id': 2, 'username': 'jack', 'password': '456789', 'gender': '三年一班', 'sex': 'male', 'phone_number': 13622222222} {'id': 3, 'username': 'jerry', 'password': '147258', 'gender': '三年二班', 'sex': 'male', 'phone_number': 13733333333} {'id': 4, 'username': 'lucy', 'password': '321456', 'gender': '三年三班', 'sex': 'female', 'phone_number': 15244444444} 1990595350992 """ # 删除数据 execute_data("delete from userinfo where username=%s", "lucy") """ 执行结果: 1990595350992 """ # 调用查询数据:查询删除数据后的数据 select_data("select * from userinfo") """ 执行结果: {'id': 1, 'username': 'tom', 'password': '123456', 'gender': '三年四班', 'sex': 'male', 'phone_number': 13511111111} {'id': 2, 'username': 'jack', 'password': '456789', 'gender': '三年一班', 'sex': 'male', 'phone_number': 13622222222} {'id': 3, 'username': 'jerry', 'password': '147258', 'gender': '三年二班', 'sex': 'male', 'phone_number': 13733333333} 1990595350992 """
ORM完整版代码
1、封装数据库连接池 mysql_pool.py
安装第三方模块:
# pip3 install DBUtils
mysql_pool.py:
''' 数据库连接池 ''' import pymysql from dbutils.pooled_db import PooledDB 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='db2', charset='utf8', autocommit=True )
2、封装MySQL类 mysql.py
mysql.py:
import pymysql from orm.orm_demo.mysql_pool import POOL # MySQL连接类 class MySQL: __instance = None @classmethod def Singleton(cls): if not cls.__instance: cls.__instance = cls() # 调用类产生对象,将对象赋值给__instance return cls.__instance # 实例化MySQL类时,获取数据库连接对象,获取游标对象 def __init__(self): self.client = POOL.connection() # 获取游标 self.cursor = self.client.cursor(pymysql.cursors.DictCursor) # 自定义查询数据方法 def select(self, sql, args=None): # 1.先提交查询sql语句 # sql = "select * from table" # sql = "select * from table where id=%s" self.cursor.execute(sql, args) # 假如查询语句中有where条件,则需要传入参数(避免sql注入,在execute中传参) # 2.获取返回的查询结果 res = self.cursor.fetchall() return res # 自定义提交修改数据方法(添加、更新、删除) def execute(self, sql, args=None): # 提交sql语句 # sql = "insert into table(id, username) values(%s, %s)" try: self.cursor.execute(sql, args) # 此处一定会传入参数 except Exception as e: print(e) # 关闭游标、关闭连接 def close(self): self.cursor.close() self.client.close()
3、ORM类 orm.py
ORM类中包括查(select_data)、增(insert_data)、改(update_data)的方法对数据进行操作
orm.py:
from orm.orm_demo.mysql 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 # varchar字段类型 class StringField(Field): def __init__(self, name, column_type="varchar(64)", primary_key=False, default=None): super().__init__(name, column_type, primary_key, default) # int字段类型 class IntegerField(Field): def __init__(self, name, column_type="int", primary_key=False, default=0): super().__init__(name, column_type, primary_key, default) # 自定义OrmMetaclass元类:控制类的创建过程 class OrmMetaclass(type): # __call__:先调用__new__,只要定义类就会触发__new__ def __new__(cls, class_name, class_bases, class_dict): # class_name:类名 class_bases:基类 class_dict:类的名称空间 # print(f"类名:{class_name}") # print(f"基类:{class_bases}") # print(f"类的名称空间:{class_dict}") # 过滤掉Models类,我们限制的是用户创建表的过程(可以说是用户创建表的规范),所以在此处过滤掉Models类,因为我们不限制Models类 if class_name == 'Models': return type.__new__(cls, class_name, class_bases, class_dict) # 第一个注意点:保证表必须要有表名。 获取table表名,若自定义则获取、没有则默认使用类名 # dict.get(key, class_name) key若有返回对应的值,若没有则返回默认值class_name就是默认值 table_name = class_dict.get("table_name", class_name) # 获取类名称空间的自定义表名table_name,若没有则默认以类名为表名 # print(table_name) # userinfo,若未定义表名,那么表名为类名,此处即User # 自定义一个主键值标识,后面做逻辑判断使用 # 主键值:主键名为 字段名 primary_key = None # 比如主键是id,那么id字段就是主键值的名字(primary_key = id) # 第三个注意点:定义一个字典用来存放 "字段名"与"字段对象" mappings = {} # print(class_dict) # 打印类中的名称空间 """ 执行结果: {'__module__': '__main__', '__qualname__': 'User', 'table_name': 'userinfo', 'id': <__main__.IntegerField object at 0x000000000287F1C8>, 'username': <__main__.StringField object at 0x000000000287F088>, 'password': <__main__.StringField object at 0x000000000287F388>} 上面class_dict的名称空间中,我们需要的仅仅是后面的id、username、password键值对属性,前两个不需要,我们需要将其剔除,后面将剔除后的结果重新放进mappings字典中 """ # 使用for循环将类中的名称空间字典 迭代依次取出 for k, v in class_dict.items(): # k为字段名,v为字段对象 # 将类中的名称空间无需的属性进行剔除筛选 # isinstance():判断一个对象是否是另一个类的实例 if isinstance(v, Field): # id、username、password的value都是Field的实例 # print(k, v) # print(v.__dict__) # {'name': 'password', 'column_type': 'varchar(64)', 'primary_key': False, 'default': None} # 第三个注意点:将所有的 "字段名" 与 "字段对象" 添加到一个定义好的独立的字典(mappings)中 mappings[k] = v """ # 此处有一个坑: # class_attr.pop(k) # 这里当字典被迭代时,不能修改其属性 """ # 第二个注意点:保证一张表只能有一个唯一的主键 # 判断字段对象如果有主键 primary_key,则为primary_key变量赋值 if v.primary_key: # 如果建的表中有主键,执行下一步,若没有不执行下一步 # 若第二次执行到此处,primary_key有值,证明有主键,抛出异常 if primary_key: raise TypeError("一张表只能有一个主键!") primary_key = v.name # 将自定义的主键值标识修改为 字段对象中的名字(和字段名一样) # 给类的名称空间 添加table_name、primary_key、mappings属性 class_dict["table_name"] = table_name # 将表名添加到类的名称空间中 class_dict["primary_key"] = primary_key # 将主键添加到类的名称空间中 class_dict["mappings"] = mappings # 将存放 "字段名"与"字段对象"的字典添加到类的名称空间中 # print(class_dict) """ 执行结果: {'__module__': '__main__', '__qualname__': 'User', 'table_name': 'userinfo', 'id': <__main__.IntegerField object at 0x000000000237F1C8>, 'username': <__main__.StringField object at 0x000000000237F088>, 'password': <__main__.StringField object at 0x000000000237F388>, 'table_name': 'User', 'primary_key': 'id', 'mappings': {'id': <__main__.IntegerField object at 0x000000000237F1C8>, 'username': <__main__.StringField object at 0x000000000237F088>, 'password': <__main__.StringField object at 0x000000000237F388>}} 上面class_dict的名称空间中,有重复的"字段名"与"字段对象",在此将class_dict中重复的"字段名"与"字段对象"过滤掉 """ # 过滤掉类名称空间中重复的字段属性 for key in mappings.keys(): # 根据mappings中的字段属性来过滤掉class_dict中的字段属性,因为mappings中的字段属性在class_dict中的字段属性都存在 class_dict.pop(key) # print(class_dict) """ 执行结果: {'__module__': '__main__', '__qualname__': 'User', 'table_name': 'userinfo', 'primary_key': 'id', 'mappings': {'id': <__main__.IntegerField object at 0x000000000283F1C8>, 'username': <__main__.StringField object at 0x000000000283F088>, 'password': <__main__.StringField object at 0x000000000283F388>}} 过滤完成! """ # 若建立的表中所有字段都没有主键(规定每一张表中都需要有一个主键),抛出异常 if not primary_key: raise TypeError("表中必须要有一个主键!") return type.__new__(cls, class_name, class_bases, class_dict) # 让Models类继承dict字典类,所有子类继承Models就等于继承了dict类 class Models(dict, metaclass=OrmMetaclass): # 在 "对象.属性" 获取属性时,若 "没有该属性" 时触发 __getattr__ def __getattr__(self, item): # print(item) return self.get(item) # 当 "对象.属性 = 属性值" ,"添加或修改属性" 时触发 __setattr__ def __setattr__(self, key, value): self[key] = value # 查询数据 @classmethod def select_data(cls, **kwargs): # name='张三' 转化为 {['name': '张三']} mysql_obj = MySQL() # 若kwargs为False代表没有查询条件 if not kwargs: # 1.查表中所有数据(select * from 表名) sql = f'select * from {cls.table_name}' # 表名可以通过传入的类来调用 res1 = mysql_obj.select(sql) return res1 else: # 获取字段名 column_name = list(kwargs.keys())[0] # 获取字段值 column_value = kwargs.get(column_name) # kwargs是一个字典 # 2.根据条件查询数据(select * from 表名 where 字段名=字段值) sql = f'select * from {cls.table_name} where {column_name}=%s' res2 = mysql_obj.select(sql, column_value) return [cls(**r) for r in res2] # 这里是把列表中的一个个字典[{}, {}...] 转化为列表中的一个个对象[obj, obj...] # 插入方法(insert into 表名(字段名1, 字段名2...) value(字段值1, 字段值2...)) def insert_data(self): mysql_obj = MySQL() # 1.获取表名 self.table_name # 2.字段名与字段值 self.mappings column_name = [] # 存放字段名的列表 [字段名1, 字段名2...] column_values = [] # 存放字段值的列表 [字段值1, 字段值2...] replace_list = [] # 设置一个替换替换值的列表 [?, ?...] for k, v in self.mappings.items(): # column_name.append(k) column_name.append(v.name) # 获取字段名追加到列表中, 是因为不知道传入的字段值有多少个 # 反射:根据字符串操作对象中的属性或方法 column_values.append(getattr(self, v.name, v.default)) # 获取字段值追加到列表中 replace_list.append('?') # 循环多少次,就说明有传入了多少个字段,列表内就有多少个 ? # sql = f'insert into {self.table_name}({",".join(column_name)}) values({",".join(replace_list)})' sql = 'insert into %s(%s) values(%s)' % (self.table_name, ",".join(column_name), ",".join(replace_list)) sql = sql.replace('?', '%s') # 将列表内的 ? 替换为 %s mysql_obj.execute(sql, column_values) # 更新方法(update 表名 set 字段名=字段值 where pk=主键值) def update_data(self): mysql_obj = MySQL() column_name = [] # 存放字段名的列表 [字段名1, 字段名2...] column_values = [] # 存放字段值的列表 [字段值1, 字段值2...] # 主键值 pk = None for k, v in self.mappings.items(): # 判断mappings中哪一个字段是主键 if v.primary_key: # 获取主键的值 pk = getattr(self, v.name) else: column_name.append(v.name + '=?') # 添加字段名 column_values.append(getattr(self, v.name)) # 添加字段值 # sql = f'update {self.table_name} set {",".join(column_name)} where {self.primary_key}={pk}' sql = 'update %s set %s where %s=%s' % (self.table_name, ",".join(column_name), self.primary_key, pk) sql = sql.replace('?', '%s') mysql_obj.execute(sql, column_values) # 假如User表中有 id、用户名和密码 三个字段 class User(Models): table_name = "user4" # 自定义一个表名,若此处不写,表名默认即为类名User # 字段类中的name属性必须与User表中类属性同名 id = IntegerField(name="id", primary_key=True) username = StringField(name="username") password = StringField(name="password") if __name__ == '__main__': # 演示查询数据 # # select_data():括号中不传参指的是 查询所有数据 # res = User.select_data() # print(res) # # select_data(name='张三'):括号中的参数代表查询条件 # user_obj = User.select_data(id=1)[0] # print(user_obj) # print(user_obj.username) # 演示插入数据 # user_obj = User(username='孙七', password='789654') # user_obj.insert_data() # 演示更新数据 # # 先查看需要修改的数据 # user_obj = User.select_data(username='孙悟空')[0] # # print(user_obj.username) # 孙悟空,获取主键id为25的用户名 # # 修改对象中属性的值,即更新数据 # user_obj.username = '孙七' # # 然后调用update_data()将更新后的数据存到数据库中 # user_obj.update_data() pass