ORM:对象关系映射
类 》》》 数据库的一张表
对象 》》》 表的一条记录
对象点属性 》》》 记录某一个字段对应的值
"""
表----映射出数据库的一张表
1、通过类实例化对象,得到表的某一个字段,基于基类Field定义两个子类StringField,IntegerField,这样定义会更明确一点
2、某一个东西某一个类怎么传参数都能实例化出来---字典dict
3、继承字典的__init__
4.对象点一个不存在的属性的时候会自动触发内部了__getattr__
5、对象点属性设置属性值得时候触发,添加或更改字典值 self[k]=v
对象---映射出表的一条记录
1、通过元类实现类的创建 __new__创建空对象的时候,获取到类里面的名称空间class_attrs
2、拿到名称空间就可以修改,可以拿到用户写的字段也可以拿到默认的
3、创建类的时候定义一个table_name,如果没有的话让表名class_name,当table_name
4、拿到所有标识表字段的key,values,如果是字段属性存放在mappings字典
5、判断是不是主键,不能有两个主键,否则报错提醒
6、循环mappings拿到的所有自定义字段名,将单个单个的字段删除
"""
orm.py代码
from mysql_singleton import Mysql
#定义一个含有字段名,字段类型,是否是主键,默认值的属性
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
#定义一个varchar字段类型
class StringField(Field):
def __init__(self,name,column_type='varchar(32)',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)
#定义一个元类
class MyMetaClass(type):
def __new__(cls,class_name,class_bases,class_attrs):
#定义的元类是用来拦截模型表的创建过程,而models并不是一张模型表,所以不需要它的创建过程
if class_name =='Models': #从子类Models拿到字段名
return type.__new__(cls,class_name,class_bases,class_attrs)
#创建类的时候定义一个table_name,如果没有的话让表名class_name,当table_name
table_name =class_attrs.get('table_name',class_name)
primary_key=None
#映射存放在字典里
mappings={}
#下面的for循环需要做两件事
# 1、将单个单个的字段整合成一个
# 2、确定当前表哪个字段是主键
#拿到所有标识表字段的key,values
for k,v in class_attrs.items():
#拿出所有自己定义的表的字段属性
#如果是字段属性存放在mappings字典
if isinstance(v,Field):
mappings[k]=v
#接着判断是不是主键
#如果是True就执行61行赋值
if v.primary_key:
#健壮性校验一张表不能有多个主键
if primary_key:
raise TypeError('一张表不能有多个主键')
primary_key =v.name #字段设为主键
#循环mappings拿到的所有自定义字段名
for k in mappings.keys():
# 最后需要把mappings加到了class_attrs里面,所以要把游离在外的删掉,用mappings存所有的
#将单个单个的字段删除
class_attrs.pop(k)
#校验用户自定义的模型表是否制定了主键字段
#如果没有一个主键报错
if not primary_key:
raise TypeError('一张表必须有一个主键')
#将标示表的特征信息,表名,表的主键字段,表的其他字段都塞到类的名称空间中
class_attrs['table_name']=table_name
class_attrs['primary_key']=primary_key
class_attrs['mappings']=mappings
return type.__new__(cls,class_name,class_bases,class_attrs)
#所有的模型表都继承Models
class Models(dict, metaclass=MyMetaClass):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __getattr__(self, item):
return self.get(item,'没有该键!')
def __setattr__(self, key, value):
self[key] = value
#查询操作最小识别单位是表
#需要查询每一张表,绑定给类,用类查询表
@classmethod
def select(cls,**kwargs):
#实例化产生一个查询对象
#做成单例模式是因为防止过多用户访问
ms=Mysql.singleton()
#查询表分两种情况
# 1、select * from %s
# 2、select * from %s where %s=%s
#第一种情况不需要传参数
if not kwargs:
sql="select * from %s"%cls.table_name
#对象点方法把sql语句传给过去返回个res
res=ms.select(sql)
else:
#第二种有参数的情况 比如name='jason' id='1'
#只能有一个过滤条件
k=list(kwargs.keys())[0]
v=kwargs.get(k)
#如果下面"?"变成"%s" 就要手动传值
#让mysql的execute函数去拼字符串
sql="select * from %s where %s=?"%(cls.table_name,k)
sql=sql.replace('?','%s')
res=ms.select(sql,v)
#如果查询到有结果
if res:
#res=[{},{},{}]
return [cls(**r) for r in res]
#**打散成:name='jason',password='123
#精髓:把mysql里的数据真正的转换成一个对象!!!
if __name__ == '__main__':
class Teacher(Models):
table_name='teacher'
tid=IntegerField(name='tid',primary_key=True)
tname=StringField(name='tname')
res1 = Teacher.select(tname='李平老师')
print(res1)
obj1 = res1[0]
print(type(obj1))
mysql_singlethon代码
import pymysql
class Mysql(object):
_instance=None
def __init__(self):
self.conn=pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='new_school',
charset='utf8',
autocommit=True
)
#游标
self.cursor=self.conn.cursor(pymysql.cursors.DictCursor)
def class_db(self):
self.cursor.close()
self.conn.close()
#这里的args是查询sql语句的过滤条件传过来的参数
def select(self,sql,args=None):
self.cursor.execute(sql,args)
res=self.cursor.fetchall() #fetchall拿到的是一个列表套字典的形式
return res
def execute(self,sql,args):
try:
self.cursor.execute(sql,args)
except BaseException as e:
print(e)
@classmethod
def singleton(cls):
#如果没有值,代表一次都没实例化
if not cls._instance:
#实例化对象
cls._instance=cls()
# 返回对象,第二次来的时候直接返回不用走36行代码,返回的值就是上一次产生的对象
return cls._instance