zoukankan      html  css  js  c++  java
  • 仿优酷项目—orm

    仿优酷项目

    一、ORM介绍

    对象关系映射,把数据库中的表数据(表名、表记录、字段)全部映射到python中。

    ​ mysql: python:

    ​ 表名 ————>类名

    ​ 记录 ————>对象

    ​ 字段 ————>对象.属性

    第一步:写字段类型的类

    	--字段名
    ​	--字段类型(长度)
    ​		--varchar(256)
    ​		--int
    ​	--是否为主键
    ​	--默认值
    
    ​	仿优酷中使用的字段类型:
    ​		—int
    ​		—string
    

    第二步:写表类

    		User:  #用户表
    ​				user_name
    ​				pwd
    
    ​		Movie:  #电影表
    ​				movie_name
    ​				movie_size
    
    ​		Notice: #公告表
    ​				title
    ​				content
    

    接下来我们解决三个问题:

    问题1: 假设100张表就需要写100个__init__。
    解决:
        继承一个Models父类
    
    问题2: 每张表的字段名与字段数量不同,导致无法直接继承Models父类
    解决:
        dict是对象,继承dict,触发字典内部的__init__(可接受任意数量以及任意类型属性)
    
    问题3: 字典的取/存值方式有限,希望改为 对象.属性取值, 对象.属性=值 存值的方式。
    解决:
        __getattr__: 取值方式
        __setattr__: 存值方式
    

    第三步:表的约束

    问题: 让所有的表类都遵循以下约束
            - 表名
            - 必须要有一个唯一主键
            - 表的字段
    
    解决:通过元类去控制类的创建,使其遵循以上约束.
    

    第四步:

     - 表名
     - 必须要有一个唯一主键
     - 表的字段
    

    接下来就开始我们的代码之旅吧!!!

    ***************************mysql_control.py******************************
    import pymysql
    
    class MySQL:
        #接下来是一个单例模式,因为每定义一个函数都要实例化mysql_control.MySQL()   
        __instance = None   #隐藏
    
        def __new__(cls, *args, **kwargs):
            if not cls.__instance:
                cls.__instance = object.__new__(cls)
            return cls.__instance
    
        def __init__(self):
            #创建数据库连接
            self.mysql = pymysql.connect(
                host = '127.0.0.1',
                port = 3306,
                user= 'root',
                password = '12345678',
                database = 'yjy',
                charset ='utf8',
                autocommit = True
            )
    
            #获取游标
            self.cursor = self.mysql.cursor(
                pymysql.cursors.DictCursor
            )
    
    
        #查看
        def select(self,sql,args=None):
            self.cursor.execute(sql,args)   #提交select语句
    
            #获取查询结果
            res = self.cursor.fetchall()
            return res
    
        #提交
        def execute(self,sql,args):
            try:
                # insert into table(name, pwd) values('yjy', '123');
                self.cursor.execute(sql,args)
    
            except Exception as e:
                print(e)
    
        def close(self):
            self.cursor.close()  #关闭游标
            self.mysql.close()  #关闭连接对象mysql
    
    ***************************orm.py******************************
    '''
    需求:
        创建一张User表,表内字段(id,name,pwd)
    '''
    # class User:   #先定义一个User类,即创建User表,orm的特性就是将表名映射成类名
    #     def __init__(self,id,name,password):
    #         self.id = id
    #         self.name = name
    #         self.password = password
    
    import mysql_control
    '''第一步:写字段类型类'''
    #父类
    # 定义这个父类的关键就在于把各个类中相同的东西拿出来,减小代码冗余
    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)  #继承父类中的这些方法
    
    #string varchar()  类型
    class StringField(Field):
        def __init__(self,name,column_type= "varchar(256)",primary_key= False,default = None):
            super().__init__(name,column_type,primary_key,default)
    
    
    '''第三步,第四步:通过元类进行表的约束'''
    
    class OrmMetaClass(type):
        def __new__(cls, class_name, class_base, class_attr):
            # 接收Models类的东西、User类的东西
            # print(class_name, class_base, class_attr)  # (类名、父类、类的名称空间)
            # print(class_attr)
    
            if class_name == "Models":
                return type.__new__(cls, class_name, class_base, class_attr)
    
            # 若不是Models则开始控制类的创建
            # 1.表名  若table_name没有,则取class_name
            table_name = class_attr.get('table_name', class_name)  # 表的名字
    
            mappings = {}  # 在python中返回的表,它是以;列表套字典的形式
    
            primary_key = None
    
            # 2.约束必须有一个唯一的主键
            for k, v in class_attr.items():
                # print(k, v)  # user_id, IntegerField(name='user_id', primary_key=True)
                if isinstance(v, Field):
                    # 3.获取所有字段,把所有表的字段都封装到一个独立的字典中mappings
                    mappings[k] = v
    
                    if v.primary_key:  # 如果字段有主键
    
                        # 判断是否有多个主键
                        if primary_key:
                            raise TypeError('只能有一个主键')  # 给他跑一个异常
    
                        primary_key = v.name
            # 剔除原表类的名称空间中重复的字段属性,目的是为了减少内存的占用
            for k in mappings.keys():
                class_attr.pop(k)
                # 如果没有主键
                if not primary_key:
                    raise TypeError('必须要有一个主键')
    
            class_attr['table_name'] = table_name
            # select * from self.table_name where id=self.primary_key。
            class_attr['primary_key'] = primary_key
            # insert into self.table_name(字段名, ...) values (字段对象.值)
            class_attr['mappings'] = mappings  # {'字段名': '字段对象'}
            # print(class_attr)
            return type.__new__(cls, class_name, class_base, class_attr)
    
    
    # 解决100张表就需要写100个__init__的问题
    class Models(dict, metaclass=OrmMetaClass):
        # 继承dict解决的是每张表的字段名与字段数量不同,导致无法直接继承Models父类
        # 继承dict,触发字典内部的__init__(可接受任意数量以及任意类型属性
        def __init__(self, **kwargs):  # **kwargs就是id,name,pwd   'name': 'yjy' --- >  name=yjy
            super().__init__(**kwargs)
    
        def __getattr__(self, item):
            # 将 字典[key]的取值方式转换为---> 字典.key的取值方式
            return self.get(item)
    
        def __setattr__(self, key, value):
            # 将字典的赋值方式转变为对象.属性的方式
            self[key] = value
    
        # 查看
        @classmethod
        def select(cls, **kwargs):  # name= 'yjy',pwd= '123'
            mysql_obj = mysql_control.MySQL()
            if not kwargs:
                # 拼接select查询语句
                sql = 'select * from %s' % cls.table_name
    
                res = mysql_obj.select(sql)
    
            # sql(查询条件):select * from User where name = yjy;
            else:
                key = list(kwargs.keys())[0]  # data_name
                value = kwargs.get(key)  # yjy
                # 拼接select查询语句
                sql = 'select * from %s where %s =?' % (cls.table_name, key)
                # 防止sql注入
                sql = sql.replace('?', '%s')
                # 提交sql,并返回查询结果
                res = mysql_obj.select(sql, value)
    
            # cls(**{key:value}) ---> cls(key=value) --> obj
    
            # 把列表套字典 ---> 列表套对象
            return [cls(**r) for r in res]  # [{}, {}, {}] ---> [obj, obj, obj..]
    
        # 插入
        def save(self):
            mysql = mysql_control.MySQL()
            # sql: insert into table(name, pwd..) values('yjy', '123', ...);
            # sql: insert into table(name, pwd..) values(?,?,?);
    
            # 获取所有的字段名
            fields = []  # 他是一个列表,我们需要用','.join() 拼接起来
    
            # 获取字段对应的值
            values = []
    
            # 替换条件
            replace = []  # [?,?]
            for k, v in self.mappings.items():
                # print(k, v)  # user_id = IntegerField(name='user_id', primary_key=True)
                fields.append(k)  # k == v.name
                values.append(getattr(self, v.name, v.default))  # getattr(User(), v.name) --> 字段值
                replace.append('?')  # [?, ?, ?...]
    
            # insert into user_info(user_id,user_name,pwd) values(?,?,?)
            sql = 'insert into %s(%s) values(%s)' % (self.table_name, ','.join(fields), ','.join(replace))
            sql = sql.replace('?', '%s')  # 防止sql注入
            mysql.execute(sql, values)  # 提交sql语句
    
        # 更新
        def sql_update(self):
            '''默认使用主键当做更新的查询条件'''
            mysql = mysql_control.MySQL()
    
            # 获取字段名
            fields = []
    
            # 获取字段名
            values = []
    
            # 获取主键的值
            primary_key = None
    
            # name=yjy, pwd='123'
            for k, v in self.mappings.items():
                if v.primary_key:
                    # 通过反射获取主键的值
                    primary_key = getattr(self, v.name)
    
                else:
                    fields.append(v.name + '=?')  # [name=?, pwd=?]
                    # 通过反射获取修改字段的值
                    values.append(
                        getattr(self, v.name)
                    )
    
            # sql: update user_info set name=yjy, pwd='123' where user_id=1;
            # sql: update user_info set name=?, pwd=? where user_id=1;
            sql = 'update %s set %s where %s=%s' % (self.table_name, ','.join(fields), self.primary_key, primary_key)
            sql = sql.replace('?', '%s')
            mysql.execute(sql, values)
    
    '''第二步:创建表类'''
    class User(Models):  #Models中已经有表的字段,所以在这里直接继承就好了
        # 自定义表名
        table_name = 'user_info'  # {'table_name': 'user_info'}
        # id, name, pwd
        user_id = IntegerField(name='user_id',primary_key=True)
        user_name = StringField(name='user_name')
        pwd = StringField(name='pwd')
    
    #注意:我们要在数据库中创建下面这样一张表
    '''
    create table user_info(
    user_id int primary key auto_increment,
    user_name varchar(256),
    pwd varchar(256));
    '''
    
    class Movie(Models):
        pass
    
    class Notice(Models):
        pass
    
    
    
    if __name__ == '__main__':
        obj = User(user_name='yjy')
        obj.save()  #插入数据
    
        user_obj = User.select(user_name='yjy')[0]  #查看数据数据
        print(user_obj)  ##{'user_id': 1, 'user_name': 'yjy', 'pwd': None}
        print(user_obj.user_name)   #yjy
    
        user_obj.user_name = 'yjyyjyyjy'
        user_obj.sql_update()
        print(user_obj.user_name)    #yjyyjyyjy
        
    #数据库中结果
    +---------+-----------+------+
    | user_id | user_name | pwd  |
    +---------+-----------+------+
    |       1 | yjyyjyyjy | NULL |
    +---------+-----------+------+
    
  • 相关阅读:
    模式应用:自定义匹配
    WPF架构学习总结
    脑子是用来想事的,不是记事的
    参加峰会“金点子”的材料
    我所想的GIX4的权限
    Process, Thread, STA, MTA, COM object
    JAVA 游戏分享 “是男人就下100层”
    关于静态方法和实例方法的一些误区。
    软区域
    Dispose, Finalization, and Resource Management
  • 原文地址:https://www.cnblogs.com/yanjiayi098-001/p/11604849.html
Copyright © 2011-2022 走看看