爬虫之数据神器10---Peewee实现ORM的核心原理

前言:

继续上一篇:爬虫之数据神器9---Peewee集成Django/Flask框架详解-CSDN博客

本章主要讲一些原理方面的东西,帮助大家在项目中 可以更好的理解!

正文:

一、模型定义

在Peewee中,模型的定义是通过模型元类(ModelMetaclass)实现的。Peewee利用Python的元类机制,在模型类定义中使用特殊的元类来创建模型类。下面我们将详细介绍模型元类的实现原理。

1.1 模型元类的实现

在Peewee中,模型元类(ModelMetaclass)是所有模型类的元类,它负责根据字段定义创建模型类属性。下面是一个简单的模型元类的实现示例:

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        fields = {}

        # 遍历模型类的属性
        for key, value in attrs.items():
            # 判断是否为字段类型
            if isinstance(value, Field):
                fields[key] = value

        # 删除原属性,将字段添加到fields属性中
        for key in fields.keys():
            del attrs[key]
        attrs['__fields__'] = fields

        # 创建新的模型类
        return super().__new__(cls, name, bases, attrs)

在这个示例中,我们通过遍历模型类的属性,判断是否为字段类型,然后将字段添加到一个名为__fields__的字典中。最后,我们删除原属性,并将__fields__属性添加到模型类中。

1.2 字段描述符

在Peewee中,每个模型字段(Field)采用描述符(Descriptor)实现。描述符是一个包含__get____set__方法的类,它可以用于控制属性的访问和修改。在模型中,字段描述符用于在获取字段值时自动调用coerce转换函数,以及在设置字段值时自动调用内部逻辑进行校验。

以下是一个简单的字段描述符的实现示例:

class FieldDescriptor:
    def __init__(self, field_name):
        self.field_name = field_name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        value = getattr(instance, self.field_name)
        # 调用coerce转换函数,返回转换后的值
        return self.field.coerce(value)

    def __set__(self, instance, value):
        # 调用内部逻辑进行校验
        self.field.validate(value)
        setattr(instance, self.field_name, value)


class Field:
    descriptor_class = FieldDescriptor

    def __init__(self, coerce=None, validate=None):
        self.coerce = coerce
        self.validate = validate

    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.name)

    def __set__(self, instance, value):
        setattr(instance, self.name, value)

在上面的示例中,我们定义了一个FieldDescriptor类作为字段的描述符,并定义了一个Field类作为字段的基类。在Field类中,我们通过__get__方法获取字段值时调用coerce转换函数,并通过__set__方法设置字段值时调用内部逻辑进行校验。

通过使用字段描述符,我们可以在模型中实现自动的转换和校验逻辑,使得模型的使用更加方便和安全。

1.3 模型类的定义

有了模型元类和字段描述符的支持,我们可以定义模型类了。下面是一个使用Peewee框架定义的示例模型类:

class User(Model):
    id = IntegerField()
    username = CharField(max_length=50)
    email = EmailField()
    created_at = DateTimeField(default=datetime.now)

    class Meta:
        database = database_instance

在这个示例中,我们定义了一个名为User的模型类。通过在模型类中定义字段,我们可以指定每个字段的数据类型和验证规则。例如,id字段使用IntegerFieldusername字段使用CharFieldemail字段使用EmailFieldcreated_at字段使用DateTimeField。这些字段都通过字段描述符自动实现类型转换和校验的功能。

我们还可以通过模型类的Meta内部类指定数据库连接。在上面的示例中,我们将database_instance作为模型类的数据库连接。

通过使用模型元类和字段描述符,我们可以方便地定义模型类,并且模型类的实例可以直接与数据库交互,进行数据的查询、插入、更新和删除等操作。

1.4 模型类的使用

一旦我们定义了模型类,我们就可以使用它来进行数据库操作。下面是一些常见的模型类使用方法:

# 创建表
User.create_table()

# 插入数据
user = User(username='John', email='john@example.com')
user.save()

# 查询数据
users = User.select().where(User.username == 'John')
for user in users:
    print(user.username, user.email)

# 更新数据
user.username = 'John Doe'
user.save()

# 删除数据
user.delete_instance()

在上述代码中,我们首先使用create_table方法创建了数据库表。然后,我们通过创建模型类的实例来插入数据,使用select方法查询数据,使用属性来更新数据,并使用delete_instance方法删除数据。

通过模型类的方法和属性,我们可以方便地进行数据库操作,而不必直接与底层的SQL语句打交道。

综上所述,模型定义是Peewee实现ORM框架的核心原理之一。模型元类和字段描述符的实现使得模型类的定义和使用变得简洁而灵活,大大提高了开发效率和数据安全性。

二、Schema转换

在Peewee中,Schema转换是指将模型类转换为数据库表的描述信息和相应的SQL语句。这一过程涉及到模型类到Schema描述类的转换以及Schema描述类到SQL语句的生成。下面我们将详细介绍这一过程的实现原理。

2.1 模型转换Schema

根据模型类生成Schema描述类是Schema转换的关键步骤之一。Peewee利用模型元类中的字段定义和元信息(如数据库连接、表名等)来创建Schema描述类。下面是一个简单的示例:

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        fields = {}

        # 遍历模型类的属性
        for key, value in attrs.items():
            # 判断是否为字段类型
            if isinstance(value, Field):
                fields[key] = value

        # 删除原属性,将字段添加到fields属性中
        for key in fields.keys():
            del attrs[key]
        attrs['__fields__'] = fields

        # 创建新的模型类
        model_class = super().__new__(cls, name, bases, attrs)

        # 创建对应的Schema描述类
        schema_class = type(name + 'Schema', (Schema,), {'model_class': model_class})

        return schema_class

在上述示例中,我们在模型元类中创建了一个新的Schema描述类,该描述类以模型类的名称加上Schema后缀命名。我们将模型类作为Schema描述类的model_class属性,以便之后生成SQL语句时可以依据模型类的信息。

通过模型转换Schema,我们可以根据模型类方便地生成相应的Schema描述类,这使得后续的SQL生成工作更加简单和直观。

2.2 Schema生成SQL

一旦我们有了Schema描述类,就可以利用它来生成相应的SQL语句。Peewee的Schema类提供了丰富的方法,可以生成创建表的SQL语句,以及查询、插入等操作的SQL语句。下面是一些示例:

# 创建表的SQL语句
sql_create_table = UserSchema.create_table()

# 查询数据的SQL语句
sql_select = UserSchema.select().where(UserSchema.username == 'John')

# 插入数据的SQL语句
user_data = {
    'username': 'John',
    'email': 'john@example.com',
}
sql_insert = UserSchema.insert(user_data)

在上述示例中,我们通过调用Schema描述类的方法生成了对应的SQL语句。例如,使用create_table方法生成创建表的SQL语句,使用select方法生成查询数据的SQL语句,使用insert方法生成插入数据的SQL语句。

通过Schema生成SQL,我们可以解耦模型类和实际的数据库操作,将数据库相关的操作封装在Schema描述类中,使得代码更加清晰和可维护。

2.3 示例:模型转换Schema和生成SQL

为了更好地理解模型转换Schema和生成SQL的过程,我们通过一个示例来演示具体的操作。

假设我们有一个名为User的模型类,其中包含以下字段:id、username和email。我们想要将该模型类转换为Schema描述类,并生成相应的SQL语句。

首先,我们定义模型类User

class User(Model):
    id = IntegerField()
    username = CharField(max_length=50)
    email = CharField(max_length=100)

    class Meta:
        database = database_instance

然后,我们通过模型元类将模型类转换为Schema描述类UserSchema

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        fields = {}

        # 遍历模型类的属性
        for key, value in attrs.items():
            # 判断是否为字段类型
            if isinstance(value, Field):
                fields[key] = value

        # 删除原属性,将字段添加到fields属性中
        for key in fields.keys():
            del attrs[key]
        attrs['__fields__'] = fields

        # 创建新的模型类
        model_class = super().__new__(cls, name, bases, attrs)

        # 创建对应的Schema描述类
        schema_class = type(name + 'Schema', (Schema,), {'model_class': model_class})

        return schema_class

# 使用模型元类转换模型类为Schema描述类
UserSchema = ModelMetaclass('User', (Model,), {})

接下来,我们可以使用Schema描述类生成相应的SQL语句:

# 生成创建表的SQL语句
sql_create_table = UserSchema.create_table()
print(sql_create_table)

# 生成插入数据的SQL语句
user_data = {
    'username': 'John',
    'email': 'john@example.com',
}
sql_insert = UserSchema.insert(user_data)
print(sql_insert)

# 生成查询数据的SQL语句
sql_select = UserSchema.select().where(UserSchema.username == 'John')
print(sql_select)

运行以上代码,我们可以得到生成的SQL语句的示例输出:

CREATE TABLE "user" ("id" INTEGER, "username" VARCHAR(50), "email" VARCHAR(100))

INSERT INTO "user" ("username", "email") VALUES ('John', 'john@example.com')

SELECT * FROM "user" WHERE "username" = 'John'

从上述输出中,我们可以看到通过Schema描述类生成的SQL语句,包括创建表的语句、插入数据的语句以及查询数据的语句。

三、查询执行

在前面的章节中,我们已经介绍了查询的构造过程,包括通过链式调用构建查询树和支持逐步添加查询条件、联表查询等操作。在本章中,我们将深入讨论查询的执行过程。

3.1 查询构造过程

查询构造过程是利用Peewee库提供的方法来逐步构建查询条件和联表查询的过程。我们可以创建一个查询对象,然后通过链式调用方法来添加查询条件和关联模型等操作。

示例代码:

from peewee import *

# 定义模型
database = SqliteDatabase(':memory:')

class BaseModel(Model):
    class Meta:
        database = database

class User(BaseModel):
    username = CharField()
    age = IntegerField()

class Article(BaseModel):
    title = CharField()
    content = TextField()
    author = ForeignKeyField(User, backref='articles')

database.create_tables([User, Article])

# 构造查询条件和关联查询
query = User.select().where(User.age >= 18)
query = query.join(Article).where(Article.title == 'Python')

# 执行查询
result = query.execute()

# 处理查询结果
for row in result:
    print(row.username, row.age)

在上述示例中,我们首先定义了两个模型 UserArticle,分别表示用户和文章。然后,我们创建了一个查询对象 query,通过调用 select() 方法来选择要查询的模型。

随后,我们通过 where() 方法添加了一个查询条件 User.age >= 18,并调用 join() 方法实现了模型之间的关联查询,即 UserArticle 之间的关系。然后,我们可以继续使用 where() 方法添加更多的查询条件,如 Article.title == 'Python'

最后,我们执行了查询并通过迭代结果来处理查询返回的数据。

3.2 查询执行

一旦我们完成了查询的构造,接下来就需要执行这个查询并获取结果。Peewee在查询的执行过程中会进行如下的转换和操作:

  1. 生成Cython查询表达式:Peewee会将查询树转换为Cython查询表达式,这样可以提高查询的性能和执行效率。通过使用Cython,Peewee可以将查询的逻辑转化为底层的原生SQL查询。

  2. 转换为原生SQL执行:生成的Cython查询表达式将被转换为原生SQL查询语句,然后由底层的数据库驱动程序执行。这样,我们可以充分利用数据库的功能和优化来执行查询。

  3. 将查询结果转化为模型对象:执行查询后,Peewee会将查询返回的结果转换为模型对象,这样我们可以方便地使用模型的属性和方法来操作查询结果。

示例代码:

from peewee import *

# 定义模型,创建数据库等

# 构造查询条件和关联查询

# 执行查询
result = query.execute()

# 处理查询结果
for row in result:
    # 处理查询结果的方式,如将结果转化为模型对象
    user = User.from_dict(row)
    print(user.username, user.age)

在上述示例中,我们执行了查询并将结果存储在 result 变量中。然后,我们可以使用适当的方式来处理查询结果。在示例中,我们将查询结果转化为模型对象,这样就可以使用模型的属性和方法来操作查询结果。

通过以上的示例和解释,我们详细介绍了查询的执行过程,包括查询构造和查询执行的关键步骤。Peewee的灵活链式调用和转换为底层原生SQL的机制,使得查询操作变得简单、高效且易于扩展。

总结:

本篇文章介绍了Peewee框架中的一些核心概念和实现原理。

  1. 首先介绍了模型元类的实现,该元类负责根据字段定义创建模型类属性,利用字段描述符实现字段的转换和校验逻辑。
  2. 然后示范了模型类的定义和使用方法,演示了创建表、插入数据、查询数据、更新数据和删除数据等常见操作。
  3. 接着讨论了模型转换Schema和生成SQL的过程,以及查询构造和查询执行的过程。通过这些内容的介绍和示例代码的演示,

大家可以了解到Peewee框架的使用方法和原理,以便更好地应用Peewee进行数据库操作。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-10 00:00:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-10 00:00:04       101 阅读
  3. 在Django里面运行非项目文件

    2024-04-10 00:00:04       82 阅读
  4. Python语言-面向对象

    2024-04-10 00:00:04       91 阅读

热门阅读

  1. Day32 线程安全二

    2024-04-10 00:00:04       35 阅读
  2. Day31 线程安全一

    2024-04-10 00:00:04       23 阅读
  3. 2024.4.7力扣每日一题——王位继承顺序

    2024-04-10 00:00:04       39 阅读
  4. python--异常处理

    2024-04-10 00:00:04       41 阅读
  5. QB/T 4464-2013 家具用蜂窝板检测

    2024-04-10 00:00:04       34 阅读
  6. vue3基础: 组件注册

    2024-04-10 00:00:04       34 阅读