python ORM实现(青铜版)

在学习django的使用过程中,发现ORM是个好东西,不需要自己去数据库创建表,也不需要写sql语句,就可以实现对数据库的增、删、查、改,于是就想,ORM的是怎么实现的呢?经过两天的努力一个小demo被我成功的“写”出来了。
(参考来自:https://blog.csdn.net/yang5102/article/details/52485977
代码如下:(写的很粗鄙,中间有很多自己不懂加的注释,初学者,见谅,见谅,有不对的地方还请指出,共同进步~~~~)
首先是Field模块,Field类是个基类,规定数据库表的字段名称:name与类型:type
CharField 继承Field,并且可以输入字符长度,IntegerField也是继承Field
定义Field基类,是在元类中实例出Field对象v时,可以直接通过v.name获取表的字段名。

class Field(object):
    def __init__(self, name,type):
        self.name = name
        self.type =type
        

    # 当打印这个类时,系统默认找类里的__str__函数去输出,
    # 如果没有这个函数,输出的就是一个对象

    def __str__(self):
        print ('%s:%s' % (self.__class__.__name__, self.name))


class CharField(Field):
    def __init__(self, name, length=10):
        self.length = length
        super(CharField, self).__init__(name, 'string')


class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'Interger')

然后是Model类的编写,Model类是各种实体类的基类,如User(Model),所以公共的.save()方法,Update()方法等都封装到Model类里。:

 class Model(dict):
    __metaclass__ = ModelMetaclass
    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []

        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('%s')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        cursor.execute(sql,args)
        db.commit()
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

最后是ModelMetaclass元类的代码:

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        mapping = dict()
        fields = []
        params = []
        keyid = []

        for k, v in attrs.items():
            # isinstance(对象,类)检测这个对象是否属于这个类
            if isinstance(v, Field) :
                # print('Found mapping :%s==>%s'% (k,v))
                mapping[k] = v


        for k in mapping.keys():
            # pop() 方法删除字典给定键 key 所对应的值,返回值为被删除的值
            attrs.pop(k)
        attrs['__mappings__'] = mapping
        for k, v in mapping.items():
            if isinstance(v, CharField):

                fields.append(v.name + ' char(%s)'% str(v.length))
            elif isinstance(v, IntegerField):
                fields.append(v.name + ' INT')

            keyid.append(k)
        attrs['__table__'] = name


        sql = 'create table IF NOT EXISTS %s (%s) ' % (name, ','.join(fields))
        print('SQL: %s' % sql)
        try:
            cursor.execute(sql)
            print("create success")
        except Exception as e:
            print("create failure")

        print('SQL: %s' % sql)
        return type.__new__(cls, name, bases, attrs)

ORM实体类的创建是通过元类动态实现的,
也就是通过 :type.__new__(cls, name, bases, attrs)实现的
cls:用来创建类的元类。
name:被创建类的名称
bases:被创建的类继承于哪些类,即被创建类的所有父类
attrs:被创建类的属性。
为什么要使用元类来创建类?而不是使用class关键字?
在python中万物皆对象,类也是对象,而元类就是类的类。
而类存在的意义就是抽象出对象共同的属性和方法。
实体类例如:User,Course......都需要创建数据库对应的表,都需要知道表字段的名称,都需要知道表名等
所以定义的元类抽象出所有实体类共同的属性和方法,来创建类。(感觉很绕啊~~~~)
程序中元类ModelMetaclass主要做了如下几件事:
1.查找实体类(比如User)的Field属性(比如CharField),找到之后保存到mapping中,然后再把mapping 保存到attr[__mapping__]中,这样在ModelMetaclass创建的类中就可以使用self.__mapping__来访问自己的Field属性。
2.保存表名到attrs['__table__'] = name
3.根据Field属性与表名创建同名表

创建User时程序编译的过程如下:


image.png

最后是完整的代码:

import pymysql
# 1.定义filed
# 2.拼接字符串
# 3.元类

db = pymysql.connect("localhost", "root", "root", "ormtest")
cursor = db.cursor()
class Field(object):
    def __init__(self, name, type):
        self.name = name
        self.type = type

    # 当打印这个类时,系统默认找类里的__str__函数去输出,
    # 如果没有这个函数,输出的就是一个对象

    def __str__(self):
        print ('%s:%s' % (self.__class__.__name__, self.name))


class CharField(Field):
    def __init__(self, name, length=10):
        self.length = length
        super(CharField, self).__init__(name, 'string')


class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'Interger')


# type(classname, parentclasses , attrs)
# classname是类名,字符串类型,parentclasses是类所有父类,元组类型,attrs是类的所有{属性:值}
# 当通过type.__new__(cls, classname, bases, attrs)创建类时,cls就是该类的元类,classname是类名,字符串类型,bases是类的父类,元组类型,attrs是类的所有{属性:值}
# 只有type和其子类可以用__new__创建类

class ModelMetaclass(type):
 
    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model :%s' % name)
        print('Found model cls :%s' % cls)
        print('Found attrs :%s' % str(attrs))
        mapping = dict()
        fields = []
        params = []
        keyid = []

        for k, v in attrs.items():
            # isinstance(对象,类)检测这个对象是否属于这个类
            if isinstance(v, Field) :
                # print('Found mapping :%s==>%s'% (k,v))
                mapping[k] = v


        for k in mapping.keys():
            # pop() 方法删除字典给定键 key 所对应的值,返回值为被删除的值
            attrs.pop(k)
        attrs['__mappings__'] = mapping
        for k, v in mapping.items():
            if isinstance(v, CharField):

                fields.append(v.name + ' char(%s)'% str(v.length))
            elif isinstance(v, IntegerField):
                fields.append(v.name + ' INT')

            keyid.append(k)
        attrs['__table__'] = name


        sql = 'create table IF NOT EXISTS %s (%s) ' % (name, ','.join(fields))
        print('SQL: %s' % sql)
        try:
            cursor.execute(sql)
            print("create success")
        except Exception as e:
            print("create failure")

        print('SQL: %s' % sql)
        return type.__new__(cls, name, bases, attrs)


# 只能python3用 metaclass=ModelMetaclass,python2使用__metaclass__ = ModelMetaclass

#  metaclass=ModelMetaclass,当编译器要创建Model类时,要从ModelMetaclass.__new__来创建
class Model(dict):
    __metaclass__ = ModelMetaclass
    def __init__(self, **kw):
       
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
       
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('%s')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        cursor.execute(sql,args)
      
        db.commit()
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))


class User(Model):
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    name = CharField('username',100)


# 创建一个实例:
u = User(id=12345, name='Michael')
# 保存到数据库:
u.save()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容