zoukankan      html  css  js  c++  java
  • ORM

    orm简单配置:

    '''
    ORM: 对象关系映射
    表 ---> 类
    一条条的记录 ---> 对象
    一条条记录下的字段 ---> 对象.属性
    
    字段类型:
        varchar:
            字段名
            字段类型
            是否为主键
            默认值
    
    从底层的字段类型开始编写
    然后创建类对象
    '''
    from mysql_control import Mysql
    
    
    # 创建字段类
    # 字段类型都需要定义一些共有的(字段名、字段类型、是否为主键、默认值)属性,所以抽象一下,先定义一个父类
    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和varchar类型的字段类型,并继承Field这个父类
    class IntegerField(Field):
        # 初始化(生成对象的时候)设置一些默认值
        def __init__(self, name, column_type='int', primary_key=False, default=0):
            super().__init__(name, column_type, primary_key, default)
    
    
    class StringField(Field):
        def __init__(self, name, column_type='varchar(255)', primary_key=False, default=None):
            super().__init__(name, column_type, primary_key, default)
    
    
    # 创建类(表)对象
    # 创建类(表)对象时候需要对其进行一定的限制--->元类可以控制创建的类
    # 定义自己的元类,名字随便(见名知意即可)
    class OrmMetaClass(type):
        # 创建类的时候需要指定三个参数    分别为   类名、类的父类、类的名称空间
        # 创建一个类一定触发 __new__ 方法   所以对此方法进行修改
        def __new__(cls, class_name, class_base, class_attr):
            # 因为我们是在创建具体一个类的时候进行限定,所以对这些类的抽象父类不进行限定
            # 过滤出抽象父类
            if class_name == 'Models':   # 这里面的Models是自己创建的抽象父类名称,可以随意同步更改
                return type.__new__(cls, class_name, class_base, class_attr)  # 如果是抽象父类,则不进行修改直接返回
    
            # 不是抽象父类
            # 需要对表(orm 将类映射为表)的  表名、主键、字段 进行限制
    
            # 1.如果这个表没有定义表名,那么就把类名当作表名
            table_name = class_attr.get('table_name', class_name)  # 使用get获取字典的值,没有值则返回默认值--->类名
    
            # 2.限定表只有一个主键
            primary_key = None  # 先将主键设为空
            mappings = {}  # 用来存放所以的表字段(仅仅存放表字段),方便取值
    
            for key, value in class_attr.items():  # 遍历类名称空间中的所有items ---> 键值对的形式
                # 将字段类的对象添加到 mappings 中
    
                # 判断 value 是否为字段类的对象
                if isinstance(value, Field):
                    mappings[key] = value  # 仅仅将表的字段名加进去,其他的类属性不要
    
                    # 判断这个表对象的主键属性是否为True
                    if value.primary_key:
    
                        # 判断主键是否已经设定(是否已经有一个主键了)---> 设定过后全局名称空间中的 primary_key 就会被修改,不在为None
                        if primary_key:  # 不为 None 则报下面的错
                            raise TypeError('only one primary_key should be set in one table!')  # 报错信息:一张表只能设置一个主键
    
                        # 没有设定则获取主键,同时全局变量 primary_key 也被修改了
                        primary_key = value.name
                    else:
                        pass
                else:
                    pass
    
            # 删除名称空间中与 mappings 重复的属性,节省资源(额外操作,可有可无)
            for key in mappings.keys():  # mappings是个字典,使用key()方法可以取出所有的 key
                class_attr.pop(key)
    
            # 判断是否有主键
            if not primary_key:
                raise TypeError('must have one primary_key!')
    
            # 对名称空间中的值进行修改(重新赋值)
            class_attr['table_name'] = table_name
            class_attr['primary_key'] = primary_key
            class_attr['mappings'] = mappings
    
            return type.__new__(cls, class_name, class_base, class_attr)
    
    
    # 元类创建好了,但是所有的表对象都需要写 init 方法,所以我们可以将 init 方法抽象为一个父类
    # 由于我们不知道子类中有多少属性,所以可以使用 **kwargs 来接收任意关键字参数--->继承字典类型
    class Models(dict, metaclass=OrmMetaClass):    # 继承字典,获取字典的方法;并指定元类,限制类的生成
        def __init__(self, **kwargs):  # **能够将字典中的键值对打散
            super().__init__(**kwargs)  # 掩盖了父类的初始化方法,就需要重新调用一下
    
        # 由于字典类没有 点属性法 查看值以及修改值,所以我们需要自己设定
    
        # 对象.属性没有时候会触发 __getattr__ 方法
        def __getattr__(self, item):
            return self.get(item, '没有这个key')  # 自定义返回值,且可以设定返回的默认值
    
        # 对象.属性 = 属性值  的时候触发 __setattr__ 方法
        def __setattr__(self, key, value):
            self[key] = value  # 功能函数内进行复制操作,达到点属性赋值一样的效果
    
        # 每个类对象(表)都应具备查询数据的功能,且应为--->非绑定方法
        @classmethod  # 非绑定方法的关键词
        def select(cls, **kwargs):  # 查看的时候一般都是关键字参数条件
            # 先获取数据库连接对象
            ms = Mysql()
    
            # 判断**kwargs,若没有则代表没有查询条件,默认为查询所有
            if not kwargs:
                # sql语句格式本应为 select * from 表名,我们先用占位符 %s 占位
                sql = 'select * from %s' % cls.table_name  # 类对象中有类名这个属性--->也就是表名
                # 接收查询的结果,且返回的结果为大列表字典类型
                res = ms.my_select(sql)  # 因为没有条件,所以不需要进行sql语句的拼接
    
            # 若有 kwargs,则代表查询时候有限制条件
            else:
                # sql语句格式本应为 select * from 表名 where 条件(kwargs的字段名=kwargs的值)
                # 分别获取sql语句所需要的所有关键信息,并生成sql语句
    
                # 1.获取条件的key,也就是字段名
                key = list(kwargs.keys())[0]  # 条件为字典,用keys()方法取值;并将结果变成有序的列表形式,依据索引取值
    
                # 2.获取条件的value,也就是字段的值
                value = kwargs.get(key)  # 字典的取值方法
    
                # 3.获取表名,也就是 cls.table_name
    
                # 生成sql语句
                sql = 'select * from %s where %s = ?' % (cls.table_name, key)  # 为了防止sql注入问题,关键的值不要手动拼接;
                # 所以先用?替换(不进行占位),然后再替换回来,再用cursor方法拼接
                sql = sql.replace('?', '%s')  # 替换的过程也是占位的过程
    
                # 接收 连接对象提交sql语句执行后返回的结果;结果为 大列表套字典 类型
                res = ms.my_select(sql, value)  # 防止sql注入问题,能自动识别sql语句的 %s
    
            # 判断返回的结果有无,并将结果以对象的形式返回
            if res:
                # 使用列表生成式,得到的是列表套对象
                return [cls(**result) for result in res]  # 将结果遍历,得到一个个字典;将字典变成对象,然后用一个大列表包裹
            else:
                pass
    
        # 插入数据
        def insert(self):
            # 先获取数据库连接对象
            ms = Mysql()
    
            # sql语句格式本应为 insert into 表名(字段名) values(各个字段对应的值); 我们先用占位符 %s 占位
            # sql = 'insert into %s(%s) values(%s)' % (...)
            # 表名---> cls.table_name
            # 字段名以及字段的值不知道
    
            # 1.定义存放字段名、字段值以及用于存放对应字段的?号的空列表
            fields = []  # 存放字段名
            values = []  # 存放字段的值
            args = []  # 存放对应字段的?号
    
            # 2.获取字段名,字段值;(过滤掉主键,因为插入数据时候不需要给主键传值)
            for k, v in self.mappings.items():  # mappings在定义元类的时候就已经限制把表中的字段名与之存放其中了
                # 过滤主键
                if not v.primary_key:  # 判断值这个对象的主键属性是否为 True,不是则继续
                    fields.append(v.name)  # 将字段的名添加进去
                    values.append(getattr(self, v.name, v.default))  # 将字段的值添加进去,没有值则添加默认值
                    # v.name 是字段名         getattr(v.name) 是字段的值---> 映射
                    args.append('?')  # 遍历循环,有多少个值就有多少个?号
    
            # 生成sql语句
            # fields与args是列表[x,x,x],但是我们的sql语句对应的形式是(x,x,x),所以进行,号拼接成目的格式
            sql = 'insert into %s(%s) values(%s)' % (self.table_name, ','.join(fields), ','.join(args))
    
            # 拼接好的sql语句中目前剩下?号这个符号,需要再次替换,以便cursor识别;同时防止sql注入问题
            sql = sql.replace('?', '%s')
    
            # 使用连接对象进行sql语句的提交(插入数据没有返回值)
            ms.my_execute(sql, values)  # 提交sql语句,并将values中的值一一对应到替换后的sql语句中
    
        # 更新数据
        def sql_update(self):
            # 先获取数据库连接对象
            ms = Mysql()
    
            # sql语句格式本应为 update 表名 set 字段名=字段值 where 字段名=字段值;    我们先用占位符 %s 占位(这里我们默认主键当作条件的字段名)
            # sql = 'update %s set %s where %s=%s' % (...)
            # 表名---> cls.table_name
            # 主键名---> cls.primary_key
            # 字段名、字段的值以及主键的值不知道
    
            # 1.定义存放字段名、字段值的空列表,并获取主键
            fields = []  # 存放字段名
            values = []  # 存放字段的值
            primary_key = None  # 先将主键的值设置为None,获取过后对其进行修改
    
            # 2.获取字段名,字段值,主键;(因为更新数据时候需要用主键定位)
            for k, v in self.mappings.items():   # mappings在定义元类的时候就已经限制把表中的字段名与之存放其中了
    
                # 获取主键的值
                if v.primary_key:  # 判断值里面主键这个属性是否为True,是就讲主键的值获取出来
                    primary_key = getattr(self, v.name, v.default)  # 获取主键的值,没有值则添加默认值
    
                # 获取其他字段名与值
                else:  # 值里面的主键属性为False,表明不是主键,也就是其他的字段
                    fields.append(v.name + '=?')  # 对字段名进行一个拼接,因为我们要设定的值是一个键值对的形式,而我们只知道字段名
                    values.append(getattr(self, v.name, v.default))  # 将字段的值添加进去,没有值则添加默认值
    
            # 生成sql语句
            sql = 'update %s set %s where %s = %s' % (self.table_name, ','.join(fields), self.primary_key, primary_key)
            # 生成后的sql语句第二个%s对应的格式为   字段名= ?   因此再对?号进行替换,以便cursor识别,防止sql注入问题
            sql = sql.replace('?', '%s')
    
            # 使用连接对象进行sql语句的提交(更新数据没有返回值)
            ms.my_execute(sql, values)  # # 提交sql语句,并将values中的值一一对应到替换
    
        # 至此每个实例化产生的对象都具备了以上的方法(包括点属性取值,赋值以及查询、插入、修改数据的方法)
    
    
    # 创建具体的对象,并继承Models,使其具备Models中的各个方法
    class User(Models):
        # 在这里不定义表名就默认表名为类名
        # table_name = 'user_info'
    
        # 每个表(类)都应具备两种数据类型---> int类型和 varchar类型   两种类型都是只有name没有默认值,所以至少传入一个name参数才能实例化出一个属性对象
        id = IntegerField(name='id', primary_key=True)  # 一般每个表都有个id字段,且一般将id设置为主键字段
        # 注意:字段名=属性名   保持一致
        # 此外,还需要一个varchar类型,我们这里以name作为另一个类属性
        name = StringField(name='name')
    
    
    # 测试
    if __name__ == '__main__':
        d3 = User(id=1, name='tank')
        print(d3)
    
        # 取值
        # print(d3.get('name'))
        # print(d3.name)
        # print(d3['name'])
    
        # 赋值(修改)
        d3.name = 'zhao'
        print(d3.name)

    mysql_control:

    import pymysql
    
    
    class Mysql:
        # 设置单例模式
        _instance = None
    
        def __new__(cls, *args, **kwargs):
            # 如果没有_instance就调用Mysql的父类Object中__nem__方法实例化对象,并赋值给_instance
            if not cls._instance:
                cls._instance = object.__new__(cls, *args, **kwargs)
                return cls._instance
    
        def __init__(self):
            # 建立连接
            self.conn = pymysql.connect(
                host='127.0.0.1',
                port=3306,
                user='user',
                passwd='123456',
                db='youku',
                charset='utf8',
                autocommit=True
            )
            # 生成游标
            self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
    
        # 关闭游标、连接的方法
        def close_db(self):
            self.cursor.close()
            self.conn.close()
    
        # 查看
        def my_select(self, sql, args=None):
            # 提交查看的sql语句
            self.cursor.execute(sql, args)
            # 接收查看结果,并返回
            res = self.cursor.fetchall()
            return res  # 返回的是字典类型数据
    
        # 提交---> 包括插入数据,修改数据
        def my_execute(self, sql, args):
            # 不一定能够提交成功,所以使用异常捕获
            try:
                self.cursor.execute(sql, args)
            except Exception as e:
                print(e)
  • 相关阅读:
    (SenchaTouch+PhoneGap)开发笔记(2)开发环境搭建二
    Sql语句复习
    冒泡排序
    微信开发订阅号(ASP.NET MVC4+jquery mobile+AppHarbor发布)
    Ext4 ComboBox组件使用
    ExtJs 进度条(轮询)
    如何替换掉.net toolStrip控件溢出按钮背景图
    easyui-menu 宽度自适应
    Python之入门学习
    servlet和filter的区别
  • 原文地址:https://www.cnblogs.com/sweet-i/p/11420837.html
Copyright © 2011-2022 走看看