ORM
存: 从代码通过ORM保存到mysql中,保存的结果是json
取: 从mysql中通过ORM取出,取出来的也是json
对象关系映射,
客户端的请求到服务端,服务端接受到指令,使用ORM对数据库进行操作,而数据库的库表都是已经存在的,ORM中操作的指令无非是对应数据库的表字段,通过操作ORM的代码来实现对表,表中字段数据的操作
一开始写的时候就是对象,然后通过ORM保存到数据库中,保存的都是json数据,但想要取出来的也是对象,所以使用元类来进行封装创建
接口层 -- 数据层 -- ORM -- 数据库
对象关系映射
类名 --- 表名
对象 --- 数据行
# 对象 通过类实例化传参得来
对象.属性 --- 字段
# 对象.属性 在类实例化的时候添加字段的属性,触发__init__
创建一个User表,表内有字段(id,name,pwd)
# 1.表名就是类名User
# 2.字段:需要在类实例化的时候添加字段(.属性)
# 实例化会触发__init__方法
# 2.1 创建字段时具有不同的类型,创建字段类型类
# - 字段名
# - 字段类型
# int
# varchar
# - 是否为主键
# - 默认值
# 定义字段类型类,继承同一父类
模型表类
创建字段类型类
# 写字段类型类
定义字段的约束:字段的名字,字段的类型,字段是否为主键,字段有无默认值
# 字段父类
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
class IntegerField(Field):
def __init__(self,name,column_type = "int",primary_key = False,default = 0):
super().__init__(self,name,column_type,primary_key,default)
# varchar
class StringFiled(Field):
def __init__(self,name,column_type = "varchar(255)",primary_key = False,default =None):
super().__init__(self,name,column_type,primary_key,default)
创建模型表类
模型表类是一张一张的表,表中有username,pwd等字段用于记录用户的数据
user表,movie表
user表中应该有 username,pwd --- 这是字段
# 创建模型表类
# 1.创建表类的重复 --- 继承
# 2.每一张表的字段数量,名字都不一样,无法继承同一父类
# --- 父类继承dict,dict是对象,继承dict,触发内部__init__(可接受任意数量已经类型属性)
# dict = {key:value} 与 dict(key=value) 结果一致
# 3.字典的取存值方式有限,改为对象.属性的方式存取值
# 存, 对象.属性没有时触发 __getattr__
# 取, 对象.属性调用时触发 __setattr__
class Models(dict):
# 1.使用字典接受所有的关键字参数
def __init__(self,**kwargs):
super().__init__(**kwargs) # kwargs接受所有的关键字参数
# 2.取值时使用getattr,self是调用者本身item是没有的属性(key)
def __getattr__(self, item):
return self.get(item)
# 3.存值时使用setattr,将调用者本身的字典中的key赋值保存为value
def __setattr__(self, key, value):
self[key] = value
class User(Models):
# def __init__(self,id,name,pwd):
# self.id = id
# self.name = name
# self.pwd = pwd
pass
class Movie(Models):
# def __init__(self,id,name,pwd):
# self.id = id
# self.name = name
# self.pwd = pwd
pass
if __name__ == '__main__':
d1=dict(name = 1) # {'name': 1}
d2 = User(name='222') #{'name': '222'}
print(d1)
# print(d2.name) # 报错
# 取值
print(d2['name']) # 222
print(d2.name) # 222
# 存值
d2['name']=1231
print(d2) #{'name': 1231}
d2.pwd = 1231
print(d2) #{'name': 1231, 'pwd': 1231}
表的约束
// 问题:让所有的表类都遵循约束,防止创建传值的失误
- 表名
- 必须有唯一主键
- 表的字段
解决: 使用元类去控制类的创建,使其遵循规则
元类控制:
- 表名
- 必须有唯一主键
- 表的字段
元类的封装
# 写字段类型类
# 字段父类
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
class IntegerField(Field):
def __init__(self,name,column_type = "int",primary_key = False,default=0):
super().__init__(name,column_type,primary_key,default)
# varchar
class StringFiled(Field):
def __init__(self,name,column_type = "varchar(255)",primary_key = False,default=None):
super().__init__(name,column_type,primary_key,default)
# 4.元类的创建
class MyMetaclass(type):
# 子类的方法覆盖父类的__init方法
# 控制了类的定义
def __new__(cls,class_name,class_base,class_dict):
# 接收models类的,与user类的所有
# print(args) # (类名,父类,类的名称空间)
# print(kwargs) 空
# print(class_name)
# print(class_base)
# print(class_dict)
# 类的所有东西
'''
Models
(<class 'dict'>,)
{'__module__': '__main__', '__qualname__': 'Models', '__init__': <function Models.__init__ at 0x0000022B6753EBF8>, '__getattr__': <function Models.__getattr__ at 0x0000022B6753EC80>, '__setattr__': <function Models.__setattr__ at 0x0000022B6753ED08>, '__classcell__': <cell at 0x0000022B674E91C8: empty>}
User
(<class '__main__.Models'>,)
{'__module__': '__main__', '__qualname__': 'User', 'table_name': 'user_info', 'user_id': <__main__.IntegerField object at 0x0000022B674EEDA0>, 'user_name': <__main__.StringFiled object at 0x0000022B674EEF28>, 'pwd': <__main__.StringFiled object at 0x0000022B674EE048>}
'''
# 1.剔除models类
if class_name == 'Models':
# 是models类就原路返回,什么也不做
return type.__new__(cls, class_name, class_base, class_dict)
# 2.设定一张表必须有一个表名
# 从类的名称空间中获取table_name的值,如果没有就以类名作为表名
table_name = class_dict.get('table_name',class_name)
# print(table_name) # user_info
# 3.主键名默认为空
primary_key = None
# 4.定义一个空字典,用于存放所有的 字段 对象
mappings = {}
# 遍历名称空间
for k,v in class_dict.items():
# print(k,v)
'''
__module__ __main__
__qualname__ User
想要的是以下字段
table_name user_info
user_id <__main__.IntegerField object at 0x00000287AB9BED68>
user_name <__main__.StringFiled object at 0x00000287AB9BEEF0>
pwd <__main__.StringFiled object at 0x00000287AB9BEF28>'''
# 过滤字段,获取想要的字段
if isinstance(v,Field):
# 给字典进行赋值保存字段
mappings[k] = v
# print(v)
''' v 就是每个字段对象
<__main__.IntegerField object at 0x000001E6B993EDD8>
<__main__.StringFiled object at 0x000001E6B993EE10>
<__main__.StringFiled object at 0x000001E6B993EF98>'''
# print(mappings)
''' 字典中字段对应的值都是对象.
{'user_id': <__main__.IntegerField object at 0x000002056198E048>, 'user_name': <__main__.StringFiled object at 0x000002056198EEF0>, 'pwd': <__main__.StringFiled object at 0x000002056198EF28>}'''
# 5.判断主键是否是唯一
# 先判断字段对象中是否存在主键,对象.属性的方法获取primary_key
if v.primary_key:
# 如果存在判断标记主键是否有值,有值说明循环过一次了
if primary_key:
raise TypeError('只能有一个主键')
# 主键标识不存在,则给primary_key赋值,防止重复
primary_key = v.name # 赋值为当前具有主键的字段名
# 6.节省资源,将mappings字典中和名称空间的重复元素删除
for k in mappings.keys():
class_dict.pop(k)
# 判断是否为主键
if not primary_key:
raise TypeError('必须有一个主键')
# 给类的名称空间中添加表名
class_dict['table_name'] = table_name
# 给类的名称空间中添加主键名
class_dict['primary_key'] = primary_key
# 给类的名称空间添加mappings字典,字典中是所有字段以及对象
class_dict['mappings'] = mappings
return type.__new__(cls,class_name,class_base,class_dict)
# 创建模型表类
# 1.创建表类的重复 --- 继承
# 2.每一张表的字段数量,名字都不一样,无法继承同一父类
# --- 父类继承dict,dict是对象,继承dict,触发内部__init__(可接受任意数量已经类型属性)
# dict = {key:value} 与 dict(key=value) 结果一致
# 3.字典的取存值方式有限,改为对象.属性的方式存取值
# 存, 对象.属性没有时触发 __getattr__
# 取, 对象.属性调用时触发 __setattr__
class Models(dict,metaclass=MyMetaclass):
# 1.使用字典接受所有的关键字参数
def __init__(self,**kwargs):
super().__init__(**kwargs) # kwargs接受所有的关键字参数
# 2.取值时使用getattr,self是调用者本身item是没有的属性(key)
def __getattr__(self, item):
return self.get(item)
# 3.存值时使用setattr,将调用者本身的字典中的key赋值保存为value
def __setattr__(self, key, value):
self[key] = value
class User(Models):
# 自定义表名
table_name = 'user_info'
# 定义字段
user_id = IntegerField(name='user_id',primary_key=True)
user_name = StringFiled(name='user_name')
pwd = StringFiled(name='pwd')
if __name__ == '__main__':
s=User()
print(s.mappings)
'''
{'user_id': <__main__.IntegerField object at 0x0000013DFA6BEDA0>, 'user_name': <__main__.StringFiled object at 0x0000013DFA6BEDD8>, 'pwd': <__main__.StringFiled object at 0x0000013DFA6BEF60>}'''
print(s.table_name) # user_info
print(s.primary_key) # user_id
数据的操作
pymysql的使用
'''pymysql的连接使用'''
import pymysql
class MySQLClient:
# 1.创建连接
def __init__(self):
# 连接客户端
self.client = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = '',
database = 'youku',
charset = 'utf8',
autocommit = True # 自动增长
)
# 创建游标对象并设置自动提交
# 类似于原来的
# conn = pymysql.connect(user='root', password='', database='oldboy')
# cursor = conn.cursor()
self.cursor = self.client.cursor(
pymysql.cursors.DictCursor # 输出字典格式
)
# 2.定义提交查询sql命令
def my_select(self,sql,value=None):
# 提交查询的sql指令
self.cursor.execute(sql,value)
# 获取查询以后得结果
res = self.cursor.fetchall()
return res
# 3.封装'提交sql命令,插入或者更新'的方法
def my_execute(self,sql,values):
try:
self.cursor.execute(sql,values)
except Exception as e:
print(e)
# 4.关闭数据库
def close(self):
self.cursor.close()
self.client.close()
select查询语句
# 查看 定义为类方法,可以类这调用
@classmethod
def select(cls,**kwargs): #**kwargs接受所有的关键字参数
# print(kwargs) # 打散关键字参数 {'name': 'tank'}
# 将MySQLClient实例化得到对象
mysql = MySQLClient()
# 定义sql语句: select * from 表名 where 条件
# 判断传入的关键字参数是否有值
if not kwargs:
# 1.不存在,直接查询所有
sql = 'select * from %s' % cls.table_name
# 调用MySQLClient接口函数查询数据
res = mysql.my_select(sql)
else:
# 2.存在,按条件查询 sql: select * from User where id=1;
key = list(kwargs.keys())[0] # 返回的是一个对象,需要转list
# print(kwargs.keys()) # dict_keys(['name'])
# print(list((kwargs.keys()))) # ['name']
value = kwargs.get(key)
# 防止sql注入问题,用户输入的都用?替代
sql = 'select * from %s where %s=?' % (cls.table_name,key)
sql = sql.replace('?','%s') # 将?替换成%s 在execute中传参防止注入
# 将sql语句与条件values传入查询函数方法
res = mysql.my_select(sql,value)
# print(res)
# 将res列表中的字典调用cls自己本身,将其转变为对象
return [cls(**d) for d in res]
运行----------------------------------
if __name__ == '__main__':
# 查
# 将select设置为类的绑定方法,可直接类调用
res = User.select(user_name = 'tank')
print(res) # [{'user_id': 1, 'user_name': 'tank', ' pwd': '123'}]
print(res[0]) # {'user_id': 1, 'user_name': 'tank', ' pwd': '123'}
print(res[0].user_name) # tank
res = User.select()
print(res) # [{'user_id': 1, 'user_name': 'tank', ' pwd': '123'}, {'user_id': 2, 'user_name': 'nick', ' pwd': '321'}]
insert 插入数据
# 插入数据
def orm_insert(self):
mysql = MySQLClient()
# sql = 'insert into 表名(字段名) values (值1,值2);
# 存储字段名
keys = []
# 存储字段对应的值,用于传参
values = []
# 存储问号,有几个?就存储几个
args = []
# print(self.mappings)
'''{'user_id': <__main__.IntegerField object at 0x000001789E2600F0>, 'user_name': <__main__.StringFiled object at 0x000001789E2602E8>, 'pwd': <__main__.StringFiled object at 0x000001789E260320>}'''
for k,v in self.mappings.items():
# print(1,k,v)
'''1 user_id <__main__.IntegerField object at 0x000001B9D25EE128>
1 user_name <__main__.StringFiled object at 0x000001B9D25EE320>
1 pwd <__main__.StringFiled object at 0x000001ECAE8DF320>'''
# 过滤掉主键,主键是自增的所以不会传值
# print(v.primary_key) # True False,v是user_id或user_name的对象,是否有.属性的primarykey方法
if not v.primary_key:
# print(v.name) # user_name pwd
# 去除掉主键之后,获取其他的
keys.append(v.name) # 字段的属性
# print(v.column_type) # column_type
# print(v.name) # user_name print(k) # user_name k=v.name
# print(keys) # ['user_name', 'pwd'] 将所有的key保存至列表中
# 存表中除了主键以外的字段值,若值没有,则使用默认值
values.append(
getattr(self,v.name,v.default) # 通过反射获得self中的name对应的值
) # self是他传进来的关键字参数,被打散为字典,也可按key取值
# print(v) # <__main__.StringFiled object at 0x000002B56E39E358>
# print(v.name,v.default) # user_name None
# print(self) # {'user_name': '小明', 'pwd': 123}
# print(self.get(v.name)) # 小明
# print(getattr(self,v.name)) # 小明
# print(values) # ['小明']
# 存放?号的,有几个字段,添加几个?
args.append('?')
# print(args) # ['?', '?']
# 编写sql语句指令
sql = 'insert into %s(%s) values (%s)' % (
self.table_name,
','.join(keys), # ['?', '?']变为 user_name,pwd
','.join(args)
)
# 将?替换
sql = sql.replace('?','%s')
# 传值到上传函数
mysql.my_execute(sql,values)
# print(keys)
----------------------------------------------------------
if __name__ == '__main__':
# 增
obj = User(user_name='nihao',pwd=123)
obj.orm_insert()
res = User.select()
print(res)
'''
[{'user_id': 1, 'user_name': 'tank', 'pwd': '123'}, {'user_id': 2, 'user_name': 'nick', 'pwd': '321'}, {'user_id': 3, 'user_name': 'nihao', 'pwd': '123'}, {'user_id': 4, 'user_name': 'nihao', 'pwd': '123'}]'''
修改更新数据
# 更新方法数据(修改数据)固定用主键当做查询方法
def orm_update(self):
print(0,self) # 0 {'user_id': 3, 'user_name': '哈哈哈', 'pwd': '123'}
'''sql = update 表名 set k1=v1, k2=v2 where id=主键值'''
myslq = MySQLClient()
# 字段名
keys = []
# 字段值
values = []
# 主键内容 id=主键名
primary_key = None # 主键名 = 主键序列号 id = 12
for k,v in self.mappings.items():
# print(0,k,v)
# 0 user_id <__main__.IntegerField object at 0x000001A110BCF1D0>
# 0 user_name <__main__.StringFiled object at 0x000001A110BCF400>
# 0 pwd <__main__.StringFiled object at 0x000001A110BCF438>
# 判断有无主键的存在,得到有主键的字段,便于where条件查询
if v.primary_key:
primary_key = v.name + '= %s' % getattr(self,v.name)
# print(1,primary_key) # 1 user_id= 3
else:
# 剩下的没有主键的字段都是想要修改的字段与新值
keys.append(v.name + '=?')
values.append(
getattr(self,v.name) # 将用户传入的参数利用getattr方法进行获得值
)
# print(2,self)
# 2 {'user_id': 3, 'user_name': '哈哈哈', 'pwd': '123'}
# 2 {'user_id': 3, 'user_name': '哈哈哈', 'pwd': '123'}
# print(3,getattr(self,v.name))
# 3 哈哈哈
# 3 123
# 语句# sql: update table set k1=?, k2=? where id=pk; #
sql = 'update %s set %s where %s' % (
self.table_name,
','.join(keys),
primary_key
)
# print(4,sql)
# 4 update user_info set user_name=?,pwd=? where user_id= 3
# 替换其中的?号
sql = sql.replace('?','%s')
myslq.my_execute(sql,values)
---------------------------------------------------------
if __name__ == '__main__':
# 修改更新
user_obj = User.select(user_name='hhh')[0]
# print(user_obj) # {'user_id': 3, 'user_name': 'nihao', 'pwd': '123'}
user_obj.user_name = '哈哈哈'
# 对象.属性获得字段,将字段赋值为'哈哈哈',调用update函数将自身传入进去,然后根据sql语句更新
user_obj.orm_update()