手撸系列之——ORM(对象关系映射)
Posted penghengshan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手撸系列之——ORM(对象关系映射)相关的知识,希望对你有一定的参考价值。
ORM:对象关系映射
类 》》》 数据库的一张表
对象 》》》 表的一条记录
对象点属性 》》》 记录某一个字段对应的值
废话不多少,先上代码:
# orm.py
from mysql_singletion 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)
# 自定义元类,用来拦截模型表的创建过程(也可使用__init__方法拦截)
class MyMetaClass(type):
def __new__(cls, cls_name, cls_bases, cls_attrs):
# models不是模型表,因此不需要拦截其产生过程,直接调用type类的__new__方法产生models类并返回结果
if cls_name == "Models":
return type.__new__(cls, cls_name, cls_bases, cls_attrs)
# 拦截模型表的闯将过程,使其增加table_name,primary_key及mappings(字段信息)的属性
table_name = cls_attrs.get("table_name",cls_name.lower())
primary_key = None
mappings = {}
#下面的for循环首先是将表的所有字段属性整合到mappings这个字典中,并确定主键字段
for k,v in cls_attrs.items():
# 取出表中所有由Field类产生的字段属性
if isinstance(v,Field):
mappings[k] = v
if v.primary_key:
if primary_key:
raise TypeError("一张表只能有一个主键!")
primary_key = v.name
# 由于已经将所有的字段属性添加到mappings中,需要将cls_attrs中的单个单个的字段属性删除
for k in mappings.keys():
cls_attrs.pop(k)
# 检验表中最终是否有主键,如没有则报错,因为一个innodb引擎的mysql表有且只有一个主键
if not primary_key:
raise TypeError("一张表必须要有主键!")
# 将下面三个属性添加至表的名称空间
cls_attrs["table_name"] = table_name
cls_attrs["primary_key"] = primary_key
cls_attrs["mappings"] = mappings
# 调用type类的__new__方法产生表那个类并返回
return type.__new__(cls,cls_name,cls_bases,cls_attrs)
class Models(dict,metaclass=MyMetaClass):
# __init__可以不写,最终也是直接调用dict类中的__init__方法去实例化,得到一个字典
def __init__(self,**kwargs):
super().__init__(**kwargs)
# 由于继承字典类,对象无法直接点属性得到对应的属性值,__getatter__在对象点 不存在的属性 时触发,
# 返回对象(也就是那个字典)属性(也就是字典中的key)对应的值,若字典中无该key则返回一个默认值。
def __getattr__(self, item):
return self.get(item,"没有该键!")
# 使对象点属性设置或修改值的时候,能在对象(字典)中添加或修改其对应的key,value
def __setattr__(self, key, value):
self[key] = value
# 类方法,表类调用进行数据的查操作
@classmethod
def select(cls, **kwargs):
# 若每次查询都实例化一次Mysql的对象,那么当成千上万的用户访问服务器时压力太大,因此在此使用单例
ms = Mysql.singletion()
if not kwargs:
sql = "select * from %s"%cls.table_name
res = ms.select(sql)
else:
# 规定sql语句只能有一个查询条件,在此使用python代码对传入的参数进行限制,只取第一对参数进行查询
k = list(kwargs.keys())[0]
v = kwargs.get(k)
# 这里的?就是一个纯粹的占位符,若一起使用%s则必须传值,因此先用?占位然后再用%s进行替换
sql = "select * from %s where %s=?"%(cls.table_name,k)
sql = sql.replace("?","%s")
res = ms.select(sql,v)
if res:
# 下面这句代码可以说是orm的精髓,把查询出来的一条条记录再实例化成对象返回
# res = [{},{},{}]
# cls(name='...',password='...')
return [cls(**r) for r in res]
if __name__ == '__main__':
class Teacher(Models):
table_name = "teacher"
tid = IntegerField(name="tid",primary_key=True)
tname = StringField(name="tname")
age = IntegerField(name="age")
t1 = Teacher(tid=1, tname="jason", age=18)
print(t1.tid,t1.tname)
print(Teacher.table_name)
print(Teacher.primary_key)
print(Teacher.mappings)
print(Teacher.select(tname="李平老师"))
'''
1 jason
teacher
id
{'id': <__main__.IntegerField object at 0x000001BBCF4DD630>, 'name': <__main__.StringField object at 0x000001BBCF4DDA20>}
[{'tid': 2, 'tname': '李平老师'}]
'''
# mysql_singletion.py
pymysql
class Mysql(object):
__instance = None
def __init__(self):
self.conn = pymysql.connect(
host="127.0.0.1",
port=3306,
user="root",
password="",
database="day41",
charset="utf8",
autocommit=True,
)
self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
def close_db(self):
self.cursor.close()
self.conn.close()
# 查找功能,传入sql代码及相应的关键参数
def select(self, sql, args=None):
self.cursor.execute(sql, args)
# 返回的结果是一个列表中包含一系列的字典,每一个字典对应数据库中的一条记录
res = self.cursor.fetchall()
return res
# 插入及修改数据功能
def execute(self, sql, args):
try:
self.cursor.execute(sql, args)
except BaseException as e:
print(e)
# 单例,使外界调用时只需实例化一个对象即可
@classmethod
def singletion(cls):
if not Mysql.__instance:
cls.__instance = cls()
return cls.__instance
需要留意的几点:
以上是关于手撸系列之——ORM(对象关系映射)的主要内容,如果未能解决你的问题,请参考以下文章
Java实现关系型数据库工具类JdbcUtils系列五:ORM对象关系映射