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