大锤python日记(元类实现ORM)

35 阅读3分钟

使用元类实现ORM的示例代码:

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

    def __str__(self):
        return f"{self.name}:{self.column_type}"


class StringField(Field):
    def __init__(self, name):
        super().__init__(name, "varchar(100)")


class IntegerField(Field):
    def __init__(self, name):
        super().__init__(name, "bigint")


class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name == "Model":
            return type.__new__(cls, name, bases, attrs)

        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                mappings[k] = v

        for k in mappings.keys():
            attrs.pop(k)

        attrs["__mappings__"] = mappings
        attrs["__table__"] = name.lower()

        return type.__new__(cls, name, bases, attrs)


class Model(metaclass=ModelMetaclass):
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    def save(self):
        fields = []
        values = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            values.append(getattr(self, k))

        sql = f"insert into {self.__table__} ({','.join(fields)}) values ({','.join(['%s']*len(values))})"
        print(f"SQL: {sql}")
        print(f"ARGS: {values}")


class User(Model):
    id = IntegerField("id")
    name = StringField("username")
    email = StringField("email")
    password = StringField("password")


user = User(id=12345, name="test", email="test@test.com", password="123456")
user.save()

这段代码定义了一个ORM框架,其中包括三个类 FieldModelMetaclassModel,以及一个使用该框架的示例类User

  1. Field类是所有数据类型的父类,它包含两个属性:字段名name和字段类型column_type。具体的数据类型需要继承Field并通过调用super().__init__()方法来初始化这两个属性。

  2. ModelMetaclass类是元类(metaclass),用于创建类时自动将类属性中的Field实例与其所属的类进行映射,并将该映射关系保存在__mappings__字典中。

  3. Model类是所有数据模型的父类,其构造函数接受一个可变关键字参数,将这些参数作为对象的属性保存起来。同时,它还有一个save方法,可以将对象保存到数据库中。

  4. User类是使用该ORM框架的示例类,继承自Model类。在该类中定义了几个属性,并指定它们的数据类型。

代码执行过程如下:

  1. 定义Field类和其子类StringFieldIntegerField。其中,StringField将所有数据都转换为字符串类型,并设置列类型为varchar(100)IntegerField将所有数据转换为整数类型,并设置列类型为bigint

  2. 定义元类ModelMetaclass,在创建新类时会自动将Field实例与其所属的类进行映射,并将该映射关系保存在__mappings__字典中。同时,还会将类名转换为小写,并将其作为表名保存到__table__属性中。

  3. 定义Model类,其中包括构造函数和save方法。构造函数接受一个可变关键字参数,在内部使用setattr方法将这些参数作为对象的属性保存起来。save方法则将对象的属性值保存到数据库中。

  4. 定义示例类User,继承自Model类。在该类中定义了几个属性,并指定它们的数据类型。

  5. 创建User类的一个对象user,传入参数id=12345, name="test", email="test@test.com", password="123456"。这些参数会被Model类的构造函数保存为对象的属性。

  6. 调用user.save()方法,将该用户对象保存到数据库中。在内部,该方法会使用__mappings__字典中存储的列名和值来构建SQL语句,并将其打印出来。最终的输出结果为:

SQL: insert into user (id,username,email,password) values (%s,%s,%s,%s)
ARGS: [12345, 'test', 'test@test.com', '123456']

这条SQL语句插入了一条记录到名为user的表中,包含4个字段:idusernameemailpassword,对应的值分别是12345'test''test@test.com''123456'

总之,该ORM框架通过元类的方式实现了自动映射和查询数据库的功能,使得编写数据模型变得更加简单和方便。