博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
手撸系列之——ORM(对象关系映射)
阅读量:6264 次
发布时间:2019-06-22

本文共 5592 字,大约阅读时间需要 18 分钟。

ORM:对象关系映射类               》》》     数据库的一张表对象             》》》     表的一条记录对象点属性        》》》     记录某一个字段对应的值
  • 思路
1、为使我们定义的类最终能像数据库中的一张表,考虑到表具有表名、主键及各字段等属性,需要为我们的类添加这些属性2、首先添加字段属性,字段具有字段名、字段类型、是否为主键及默认值等属性,因此先创建字段类,后面通过字段类实例化产生一个个的字段对象作为表类中的属性3、当类中的字段属性创建出来后,由于每个字段就是一条属性且是单个单个存在,不方便我们一次性查出所有的字段,因此我们通过自定义元类控制表类的产生过程,将table_name,primary_key,mappings整合出来作为类中的三个属性分别对应数据库中表的表名,主键及字段4、由于我们每个项目中有许多不同的表,不想每个类中都写__init__方法,然后就继承一个功能类MOdels,且当类实例化时想无论传入多少组(key=value)参数都能产生一个对象,因此通过继承dict类实现该功能,当Modells类由自定义元类产生时,就会使所有表类都由自定义元类控制产生,最终都具有上面说的三个属性5、此时虽然可以通过类实例化出一个个对象,对应着表中的一条条数据,但是却无法通过 对象.属性 的方法查看、修改对应的值,然后就在Models类中添加__getattr__和__setattr__方法,使 对象.属性 能间接查看,修改值。6、最后就是封装数据库命令(select,update,save等)

废话不多少,先上代码:

# orm.pyfrom 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 pymysqlclass 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

需要留意的几点:

5ce29e7e9499f10790.jpg

5ce29e813fc6b36635.jpg

5ce29e8384adf98975.jpg

5ce2a11d412a687990.jpg

转载于:https://www.cnblogs.com/penghengshan/p/10896342.html

你可能感兴趣的文章
手工注入 mutillidae
查看>>
线程间的协作(2)——生产者与消费者模式
查看>>
实战1:对lager测试来分析Erlang的一些特性
查看>>
input checkbox 复选框大小修改
查看>>
【iOS 开发】初识函数式 Swift 实用
查看>>
Java企业微信开发_Exception_02_java.security.InvalidKeyException: Illegal key size
查看>>
9月4日云栖精选夜读 | 初学者的问题:在神经网络中应使用多少隐藏层/神经元?(附实例)...
查看>>
CentOS 7安装完成后基本配置
查看>>
Android弹窗二则: PopupWindow和AlertDialog
查看>>
Nginx+uWsgi生产部署Django
查看>>
Linux磁盘管理之创建磁盘分区05
查看>>
【星云测试】开发者测试-采用精准测试工具对Spring Boot应用进行测试
查看>>
地图控件省份显示不全问题解决办法?
查看>>
使用Python检测并绕过Web应用程序防火墙
查看>>
C#创建dll类库
查看>>
ES6(Generator)
查看>>
坚持在简书写博客的好处
查看>>
HTML 知识点总结
查看>>
11- vue django restful framework 打造生鲜超市 -用户登录和手机注册(下)
查看>>
玩转Arduino之开篇
查看>>