Python实现一个ORM模型类

时间:2024-02-01 18:27:50

ORM是三个单词首字母组合而成,包含了Object(对象-类),Relations(关系),Mapping(映射)。解释过字面意思,但ORM的概念仍然模糊。私以为要理解一个事物,最好的法子是搞明白它出现是为了解决什么问题。

一个简单的ORM模型

我们也可以通过元类来实现自己的ORM。下面将涉及两个知识点:元类,描述符。

首先完成属性描述符的设计:

class BaseFiled(object):
    pass


class CharFiled(BaseFiled):
    def __init__(self, max_len=10):
        self.max_len = max_len

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if isinstance(value, str):
            # 判断类型进行控制
            if len(value) <= self.max_len:
                self.value = value
            else:
                raise TypeError('超出最大长度')
        else:
            raise TypeError('need a str')

    def __delete__(self, instance):
        self.value = None


class IntFiled(BaseFiled):
    def __init__(self, max_len=10):
        self.max_len = max_len

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if isinstance(value, int):
            # 判断类型进行控制
            if len(str(value)) <= self.max_len:
                self.value = value
            else:
                raise TypeError('超出最大长度')
        else:
            raise TypeError('need a int')

    def __delete__(self, instance):
        self.value = None


class BoolFiled(BaseFiled):
    def __init__(self, max_len=10):
        self.max_len = max_len

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if isinstance(value, bool):
            # 判断类型进行控制
            self.value = value
        else:
            raise TypeError('need a bool')

实现一个元类:

class FieldMetaClass(type):
    # 创建模型类的元类
    def __new__(cls, name, bases, dic, *args, **kwargs):
        if name == 'BaseModel':
            return super().__new__(cls, name, bases, dic)
        table_name = name.lower()
        # 将类名转换成小写,对应数据表的名称
        fields = {}
        for k, v in dic.items():
            # 判断value的类型是不是BaseFiled类型的 因为调用的类的父类就是BaseFiled 所以通过CharFiled等创建出来的对象也就是BaseFiled类型的
            if isinstance(v, BaseFiled):
                fields[k] = v
        dic['t_name'] = table_name
        dic['fields'] = fields
        # 将类名和属性取出来放在一个字典中
        return super().__new__(cls, name, bases, dic)

给模型类创建一个父类,具体原因及作用可以观察注释:

class BaseModel(metaclass=FieldMetaClass):
    # 模型类的父类
    def __init__(self, **kwargs):
        # kwargs 传入的是一个字典
        for k, v in kwargs.items():
            setattr(self, k, v)
            # setattr 设置属性  传入对象、属性名、属性值

    def save(self):
        # 保存一条数据,生成一条对应的sql语句
        # 获取表名
        t_name = self.t_name
        # 获取字段名称
        fields = self.fields
        # 获取对应字段的值
        filed_dict = {} # 创建一个字典用来存储键值对
        for filed in fields.keys():
            value = getattr(self, filed)
            # 把遍历出来的键中的值找到
            filed_dict[filed] = value

        # 生成对应的sql语句
        sql = 'INSERT INTO {0} VALUES {1};'.format(t_name, tuple(filed_dict.values()))
       

生成模型类:

class User(BaseModel):
    # 用户模型类 在模型类中不会重写init方法,在它的父类中写init方法 它会自动继承
    username = CharFiled()
    pwd = CharFiled()
    age = IntFiled()
    live = BoolFiled()

调用方式:

a = User(username='12', pwd='19', age=20, live=True)
a.save()

这样就实现了一个模型类,注释写的个人感觉还是比较清晰的,有不清楚的欢迎留言交流