python 元类 orm_1105 python利用元类实现ORM框架

ROM框架

ORM对象映射关系

类名 -- 表名

对象 -- 记录

对象.属性 --- 字段

1.定义表字段的类型

2.创建元类,限制表类的创建

'''

元类需要处理的问题:

1.一张表必须要有一个表名。

2.给数据表类,强制必须要有一个主键,主键必须是唯一的。

3.将数据表中,所有的字段对象,都存放在一个独立的字典中

存不是目的,目的是为了取值方便。

'''

过滤Models类,models类中,什么都不做,将类原路返回。

一张表必须有表名,如果没有则将类名做表名

表名

主键名

定义一个空字典,用来存放字段对象

遍历名称空间的属性判断主键的存在与否

节省资源:mappings字典中与原名称空间中有属性重复,提出属性

3.创建用户表类,继承dict与元类,以解决表类中数据的不一致传参,和主键的限制

'''

ORM对象映射关系

类名 -- 表名

对象 -- 记录

对象.属性 --- 字段

'''

# 1.创建字段的类型

# 创建字段时,create table id int primary key default

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)

# 创建str类型

class StringField(Field):

def __init__(self,name,column_type='varchar(64)',primary_key=False,default=None):

super().__init__(name,column_type,primary_key,default)

'''

元类需要处理的问题:

1.一张表必须要有一个表名。

2.给数据表类,强制必须要有一个主键,主键必须是唯一的。

3.将数据表中,所有的字段对象,都存放在一个独立的字典中

存不是目的,目的是为了取值方便。

'''

# 2.创建元类并限制表类的创建条件

class Mymetaclass(type):

def __new__(cls,class_name,class_base,class_dict):

# print(class_name) # 类名

# print(class_base) # 父类

# print(class_dict) # 类的名称空间(属性)

if class_name == 'Models':

# models类中,什么都不做,将类原路返回。

return type.__new__(cls, class_name, class_base, class_dict)

# 1.表名

table_name = class_dict.get('table_name',class_name)

# 2.主键名

key = None

# 3.定义一个空字典,用来存放字段对象

mappings = {}

# 遍历名称空间中的所有属性

for k ,v in class_dict.items():

# print(k,v) # 除了有字段,还有其他字段以外的属性

'''

__module__ __main__

__qualname__ User

user_id <__main__.integerfield object at>

user_name <__main__.stringfield object at>

pwd <__main__.stringfield object at>'''

# 过滤字段对象以外的内容

if isinstance(v,Field):

mappings[k] = v # 保存至字段字典中

# print(k,v)

'''user_id <__main__.integerfield object at>

user_name <__main__.stringfield object at>

pwd <__main__.stringfield object at>'''

# print(mappings)

'''{'user_id': <__main__.integerfield object at>}

{'user_id': <__main__.integerfield object at>, 'user_name': <__main__.stringfield object at>}

{'user_id': <__main__.integerfield object at>, 'user_name': <__main__.stringfield object at>, 'pwd': <__main__.stringfield object at>}'''

# 判断字段对象primary_key是否为True,然后再判断设置的key有没有值

if v.primary_key:

# 再判断初始的key是否有值

if key:

# 如果有值代表有两个主键

raise TypeError('只能有一个主键')

# 若对象字段主键不存在,则给key赋值,说明主键字段有值了

key = v.name

# print(v.name) # user_id

# 判断是否有主键

if not key:

raise TypeError('必须有一个主键')

#

# # 判断是否有主键

# if not key:

# raise TypeError('必须有一个主键')

# 节省资源:mappings字典中与原名称空间中有属性重复,提出属性

for k in mappings.keys(): # 遍历所有的keys,去除class_dict 中的相同值

class_dict.pop(k) # pop 删除dict中对应k的键值对

print(class_dict)

'''{'__module__': '__main__', '__qualname__': 'User'}'''

# 给类的名称空间添加表名

class_dict['table_name'] = table_name

# 给类的名称空间添加主键名

class_dict['primary_key'] = key

# 给类的名称空间添加一个mappings字典,字典拥有所有字段属性

class_dict['mappings'] = mappings

# print(class_dict)

'''

{'__module__': '__main__', '__qualname__': 'User', 'table_name': 'User', 'primary_key': 'user_id', 'mappings': {'user_id': <__main__.integerfield object at>, 'user_name': <__main__.stringfield object at>, 'pwd': <__main__.stringfield object at>}}'''

# 返回__new__创建的对象

return type.__new__(cls,class_name,class_base,class_dict)

# 3.创建用户表类,继承dict与元类,以解决表类中数据的不一致传参,和主键的限制

class Models(dict,metaclass=Mymetaclass):

def __getattr__(self, item):

return self.get(item)

def __setattr__(self, key, value):

self[key] = value

# 用户表

class User(Models):

# 类名就是表名,类的属性(对象)就是字段.

user_id = IntegerField(name='user_id',primary_key=True)

user_name = StringField(name='user_name')

pwd = StringField('pwd')

pass

if __name__ == '__main__':

User()

# print(User.__dict__)

model中定义 查找,插入,更新,删除的方法

'''

ORM对象映射关系

类名 -- 表名

对象 -- 记录

对象.属性 --- 字段

'''

from MySQL_control import MySQL

# 1.创建字段的类型

# 创建字段时,create table id int primary key default

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)

# 创建str类型

class StringField(Field):

def __init__(self,name,column_type='varchar(64)',primary_key=False,default=None):

super().__init__(name,column_type,primary_key,default)

'''

元类需要处理的问题:

1.一张表必须要有一个表名。

2.给数据表类,强制必须要有一个主键,主键必须是唯一的。

3.将数据表中,所有的字段对象,都存放在一个独立的字典中

存不是目的,目的是为了取值方便。

'''

# 2.创建元类并限制表类的创建条件

class Mymetaclass(type):

def __new__(cls,class_name,class_base,class_dict):

# print(class_name) # 类名

# print(class_base) # 父类

# print(class_dict) # 类的名称空间(属性)

if class_name == 'Models':

# models类中,什么都不做,将类原路返回。

return type.__new__(cls, class_name, class_base, class_dict)

# 1.表名

table_name = class_dict.get('table_name',class_name)

# 2.主键名

primary_key = None

# 3.定义一个空字典,用来存放字段对象

mappings = {}

# 遍历名称空间中的所有属性

for k ,v in class_dict.items():

# print(k,v) # 除了有字段,还有其他字段以外的属性

# 过滤字段对象以外的内容

if isinstance(v,Field):

mappings[k] = v # 保存至字段字典中

# print(k,v)

# print(mappings)

# 判断字段对象primary_key是否为True,然后再判断设置的key有没有值

if v.primary_key:

# 再判断初始的key是否有值

if primary_key:

# 如果有值代表有两个主键

raise TypeError('只能有一个主键')

# 若对象字段主键不存在,则给key赋值,说明主键字段有值了

key = v.name

# print(v.name)

# # 判断是否有主键

# if not primary_key:

# raise TypeError('必须有一个主键')

# 节省资源:mappings字典中与原名称空间中有属性重复,提出属性

for k in mappings.keys(): # 遍历所有的keys,去除class_dict 中的相同值

class_dict.pop(k) # pop 删除dict中对应k的键值对

# print(class_dict)

# 判断是否有主键

if not primary_key:

raise TypeError('必须有一个主键')

# 给类的名称空间添加表名

class_dict['table_name'] = table_name

# 给类的名称空间添加主键名

class_dict['primary_key'] = primary_key

# 给类的名称空间添加一个mappings字典,字典拥有所有字段属性

class_dict['mappings'] = mappings

# print(class_dict)

# 返回__new__创建的对象

return type.__new__(cls,class_name,class_base,class_dict)

# 3.创建用户表类,继承dict与元类,以解决表类中数据的不一致传参,和主键的限制

class Models(dict,metaclass=Mymetaclass):

def __getattr__(self, item):

return self.get(item)

def __setattr__(self, key, value):

self[key] = value

# 查找,插入,更新,删除的方法

# 查找 (将查询封装为类方法,由类直接调用查找)

@classmethod

def select(cls,**kwargs): # 利用**kwargs打散关键字参数为字典

# print(kwargs)

# 调用Mysql拿到对象 mysql

mysql = MySQL()

# 查询指令有全部查询,与按条件where查询

# 1.sql = 'select * from table;'

if not kwargs: # 如果没有where 条件的指令走这里

sql = 'select * from %s ' % cls.table_name # 通过类方法获得类名

res = mysql.my_select(sql) # 调用client的select方法

else: # 有值的话证明需要where 条件查询

# 2.sql = 'select * from table where 条件;'

key = list(kwargs.keys())[0] #获取传入的参数(list类型转换为列表)取值

# print(list(kwargs.keys()))

value = kwargs.get(key) # 按key取值获取传入的value

# 查询的条件语句

sql = 'select * from %s where %s=? ' % (cls.table_name,key)

# 使用%s 替换?, ? 的作用是防止用户输入时的sql注入

sql = sql.replace('?','%s')

# 拿到mysql的游标,提交sql语句

res = mysql.my_select(sql,value)

# d = {'id':1,'name':2} ** 可以将字典打散传入函数中为关键字参数

return [cls(**d) for d in res] #return出去的就具有.属性的方法取值

# 插入 User(传关键字参数) ---》 user_obj.insert(user_obj)

def insert(self): # 对象调用传入参数,用户输入的是值,字段名设置好的

# 调用mysql 拿到对象

mysql = MySQL()

# insert into table (字段名) values (值);

# 其中 字段可能有多个值,传值也有多个,所以用%s进行替代

# 存储所有的字段名

keys = []

# 存字段对应的值

values = []

# 存放?号的,有几个字段,就存在几个?号,替换条件'???'

args = []

# mappings存放的是字段的对象,通过items 循环取出

for k,v in self.mappings.items():

# print(k,v) # user_id = IntegerField(name='user_id',primary_key=True)

# 过滤掉主键,因为主键是自增的,不需要添加

if not v.primary_key:

# 存表中除了主键以外的字段名

keys.append(v.name) # k == v.name

# 存表中除了主键以外的字段值,若值没有,则使用默认值

values.append( getattr(self , v.name , v.default) ) # 通过反射才能找到传的值

# 这里values获取的是用户传来的所有关键字参数的值,也就是修改的值.

# 存放?号,有几个字段,就存几个?号,用来替换用户输入的字符

args.append('?')

# print(keys)

# print(values)

# print(args)

# sql: insert into table_name(v1, v2, v3) values(?, ?, ?)

sql = 'insert into %s(%s) values (%s);' % (

self.table_name,

','.join(keys),

','.join(args)

)

sql = sql.replace('?','%s')

# sql: insert into table_name(v1, v2, v3) values(%s, %s, %s)

# 封装一个my_execupt方法,用于发送指令

mysql.my_execupt(sql,values)

# mysql.cursor.execute(sql, values)

# 更新

def sql_update(self):

# 默认使用主键当做更新的查询条件 primary_key的名字是主键名,主键

mysql = MySQL()

# 字段名

keys = []

# 字段值

values = []

# 主键 id = pk --- id = 1 这样的

primary_key = None # 主键可能不一样,所以需要主键名

# 获取所有的字段名以及字段的对象

for k,v in self.mappings.items():

# 判断如果是主键的话进行赋值操作

if v.primary_key:

# 定义拼接主键为: 具有主键的名(id) + '=' + 利用反射id获取的主键的对应值

primary_key = v.name + '=%s' % getattr(self,v.name)

print(v.name)

print(getattr(self,v.name))

# 其他的都是需要修改的值(除了主键剩下的都是字段了)

else:

# 获取所有的字段的名字

keys.append(v.name + '=?') # ? 则是用户输入的值,传值时需要替换,再传入values

# 通过getattr 反射获得所有字段的值

values.append(getattr(self,v.name))

# sql: update table set k1=?, k2=? where id=pk; #

# sql = update table set key=value, k2=v2 where user_id = 1;(这里将主键固定为条件)

# 将where 条件后的'主键字段=主键值' 替换为一个 %s

sql = 'update %s set %s where %s' % (

self.table_name,

','.join(keys),

primary_key

)

# 替换"?"

sql = sql.replace('?','%s')

# 调用mysql中的my_execupt上传方法

mysql.my_execupt(sql,values)

# 用户表

class User(Models):

# 类名就是表名,类的属性(对象)就是字段.

user_id = IntegerField(name='user_id',primary_key=True)

user_name = StringField(name='user_name')

pwd = StringField('pwd')

pass

if __name__ == '__main__':

# 增 orm_insert

# User表 ---> 添加字段对应的数据

obj = User(user_name='小明', pwd='123')

obj.insert()

# 查询

# res1 = User.select() # 查询所有

# print(1,res1)

# res2 = User.select(user_name='小明')

# print(2,res2)

# 改

# obj = User.select()

# print(obj.user_name)

# obj.user_name = '小红'

# obj.sql_update()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值