python 其实不用框架你也可以实现ORM

"""
ORM思路归纳:

    1、将每张表映射成不同的表类,一个表类映射一张表。表类的类属性
        需要有表名、字段名
        将每个字段类型映射为字段类型类,一个字段类型类映射一个类
        字段类实例化需要传入有字段名、字段类型、是否是主键、默认值
        等参数,其中除字段名外其他参数可设置默认值
    2、每个表类都有相同的基础属性,如表名,主键字段,增删改查方法,
        那么由此就可以抽离出Models基类,相同属性和方法同一抽到基类,
        不涉及需要每个表区别属性的方法我们把它抽为基类类方法。
    3、考虑到一条数据库数据需要产生一个对象,用pymysql查询得到得可以
        是字典类型,那么我们可以将基类Models继承dict,用__setattr__
        将字段转换为伪对象属性操作,用__getattr__对属性取值
        由基类查询一条或者多条数据的类方法,将得到的记录本身传给表类
        自身实例化得到对象,用__setattr__将字段作为对象属性,这样一
        个对象就可以映射一条数据
    4、为了更好的实现对数据库表的映射,我们需要创建元类来对定义的表类
        的控制,如一个表只且只有一个主键,规范每个表类的属性名称,比如
        表名、主键名,其他字段集合
    5、另外要实现对数据库操作提交,这部分功能不适合做在基类表类中,做在
        表类中会让表类臃肿且逻辑不清,我们将其专门定义一个数据库连接类,
        表这一部分就负责实现功能,具体对数据库通信操作由数据库连接类去完成

"""

# 实现代码

class Field(object):
    """
    Field:映射数据库字段 基类
    name:字段名
    culomn_type:字段类型
    primary_key:是否是主键
    default:默认值
    """
    def __init__(self,name,culomn_type,primary_key,default):
        self.name = name
        self.culomn_type = culomn_type
        self.primary_key = primary_key
        self.default = default

class IntField(Field):
    """
    继承Field基类,实现对int类型字段的映射
    """
    def __init__(self,name,culomn_type = "int",primary_key = False,default = 0):
        super().__init__(name,culomn_type,primary_key,default)


class PrimaryField(Field):
    """
        继承Field基类,实现对主键字段的映射
    """
    def __init__(self,name,culomn_type,primary_key = True, default = None):
        super().__init__(name,culomn_type,primary_key,default)


class StringField(Field):
    """
        继承Field基类,实现对char类型字段的映射
    """
    def __init__(self,name,culomn_type = "varchar(64)", primary_key = False,default = ""):
        super().__init__(name,culomn_type,primary_key,default)

# 你还可以继续定义其他类型字段类,如日期等





class MyMeta(type): """ 自定义元类,实现对每张表映射的表类的属性控制 如:1、限制一个表有且只能有一个主键,否则创建派生类失败 2、将表的字段属性全部放到一个字典里,然后把这个字典整体放入类的名称空间 """ def __new__(cls, class_name, class_bases, class_space): # 控制类产生 if class_name == "Models": # 对 Models类不生效 # 所有表类由Models类派生 return type.__new__(cls,class_name,class_bases, class_space) table_name = class_space.get("table_name") primary_key = None mappings = {} for k,v in class_space.items():# 筛选出主键 if isinstance(v,Field): mappings[k] = v if v.primary_key: if primary_key: raise TypeError("一个表只能有一个主键") primary_key = v for key in mappings.keys(): # 将原名称空间中的所有字段属性删除 class_space.pop(key) class_space["table_name"] = table_name class_space["primary_key"] = primary_key # 将筛选出来的属性重新装回类名称空间 class_space["mappings"] = mappings # 将字段属性装入mapping再装回原名称空间 return type.__new__(cls,class_name,class_bases,class_space) # 调回type__new__ class Models(dict,metaclass = MyMeta): 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): # 查询表单条或多条记录,并实例化记录为对象返回 mysql = Mysql.create_instance() if not kwargs: sql = "select * from %s"%cls.table_name res = mysql.select(sql) else: k = list(kwargs.keys())[0] v = kwargs.get(k) sql = "select * from %s where %s = $"%(cls.table_name,k) sql = sql.replace("$","%s") res = mysql.select(sql,(v,)) if res: return [cls(**r) for r in res] import pymysql class Mysql(object) : _instance = None def __init__(self): # 建立数据库连接 self.conn = pymysql.connect( host = "127.0.0.1", port = 3306, user = "root", password = "123456", database = "class", charset = "utf8", autocommit = True ) self.cursor = self.conn.cursor(pymysql.cursors.DictCursor) # 设置查询返回数据格式为列表套字典 @classmethod def create_instance(cls): # 创建单例 if not cls._instance: cls._instance = cls() return cls._instance def select(self,sql,arg = None): # 提交数据库操作 if not arg: try: self.cursor.execute(sql) except BaseException as e: print(e) return else: try: self.cursor.execute(sql,arg) except BaseException as e: print(e) return res = self.cursor.fetchall() # 拿到所有返回结果 return res if __name__ == '__main__': class Course(Models): # course表类 table_name = "course" cid = PrimaryField(name = "cid",culomn_type="int") # 表id字段 创建主键字段对象 cname = StringField(name = "cname") # 表cname字段,创建字符串类型对象 res = Course.select(cid = 3) # 查找cid = 3的记录并实例化对象返回 print(res[0].cname) print(res[0].mappings)

猜你喜欢

转载自www.cnblogs.com/dongxixi/p/10897254.html