首页 > 其他分享 >爬虫之数据神器10---Peewee实现ORM的核心原理

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

时间:2024-04-10 09:30:28浏览次数:23  
标签:__ 10 Peewee 模型 查询 --- SQL class Schema

前言:

继续上一篇:爬虫之数据神器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='[email protected]')
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': '[email protected]',
}
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': '[email protected]',
}
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', '[email protected]')

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进行数据库操作。

标签:__,10,Peewee,模型,查询,---,SQL,class,Schema
From: https://blog.csdn.net/m0_56758840/article/details/137570118

相关文章

  • 无主之地2丢失msvcr100.dll怎么办?无主之地2丢失msvcr100.dll问题的全方位解决指南
    在计算机系统的日常运行与维护过程中,我们可能会遇到一种特定的故障场景,即系统中关键性动态链接库文件msvcr100.dll的丢失。那么无主之地2丢失msvcr100.dll应该怎么解决呢?下面一起来看看具体的解决方法介绍吧!一、丢失原因1.安装软件失败:当我们尝试安装某个软件时,可能会遇到......
  • 【SpringBoot + Tomcat】【二】请求到达后端服务进程后的处理过程-连接的处理细节
    1 前言上节的后半部分,由于忙项目的事情去了,收尾的有点潦草,我们这节再继续。上节我们的思路是先简单回顾了下,SpringBoot启动和创建Tomcat的时机,然后我们还主要看了下连接器Connector的创建已经启动过程。连接器本身很重要,因为它就像一个港口或者一个枢纽,连接着客户端和服......
  • DMP6300-220/20Z电力直流屏高频充电模块
    DMP6300-220/20Z直流屏充电模块是一种高性能的充电设备,专为电力系统直流屏设计。该模块采用了先进的开关电源技术,具有高效率、高稳定性的特点,能够有效提高电源利用效率,同时降低能源浪费。作为一款智能化的充电模块,DMP6300-220/20Z具备实时监测电源状态、调整输出电压和电流的......
  • 模型压缩与部署-书生浦语大模型实战营学习笔记&大语言模型7
    大语言模型-7.模型压缩与部署书生浦语大模型实战营学习笔记4-模型压缩与部署本文包括第二期实战营的第5课内容,介绍关于模型压缩的相关内容,主要包括、模型量化和模型部署的相关内容。模型部署定义:在软件工程中,部署通常指的是将开发完毕的软件投入使用的过程。在人工智能领域,模......
  • python web 开发 - 基于flask框架的 Hello World 示例
    pythonweb开发-基于flask框架的HelloWorld示例文章目录pythonweb开发-基于flask框架的HelloWorld示例1、主要步骤2、flask安装3、创建程序4、运行程序5、通过浏览器访问1、主要步骤(1)安裝flask:pip3installflask(2)編寫並......
  • 最短编辑距离(线性dp)-java
    最短编辑问题也是一种非常经典的二维线性dp问题。 文章目录前言一、最短编辑距离问题二、算法思路1.dp[i][j]的情况 2.边界问题初始化3.状态转移方程三、代码如下1.代码如下2.读入数据3.代码运行结果总结前言最短编辑问题也是一种非常经典的二维线性dp问......
  • 大数据技术与应用课堂测试 -神经网络计算过程
    石家庄铁道大学2024年春季  2021级大数据技术与应用课堂测试-神经网络计算过程课程名称: 大数据技术与应用  任课教师:王建民      1、用自己的话说明深度学习训练三部群正向传播,反向传播,梯度下降的基本功能和原理。 (1)正向传播是输入数据通过神经网络,从输入层......
  • 4月10课堂测试
    用自己的话说明深度学习训练三部群正向传播,反向传播,梯度下降的基本功能和原理。        正向传播:正向传播是指输入数据通过神经网络的各个层,最终得到输出结果的过程。在正向传播中,每一层的神经元将输入数据进行加权求和,并经过激活函数处理后传递给下一层。神经网......
  • 【可视化大屏开发】17. 加餐-ECharts定制省份地图
    各身份地图数据下载小工具DataV.GeoAtlas地理小工具系列 登录DataV后,直接通过选择点击获取需要的省份地区数据>其实单击即可完成选择检查下载的数据格式是否正常自定义字体更新地图部分代码index.less部分//声明字体@font-face{font-family:electron......
  • Transaction rolled back because it has been marked as rollback-only
    背景最近在看程序日志的时候,发现频繁出现Transactionrolledbackbecauseithasbeenmarkedasrollback-only这个异常,查了很久资料才知道是什么原因导致抛出这异常的,下面解析一下;原因字面上的意思就是:事务已回滚,因为它已被标记为仅回滚,那为什么会标记为仅回滚呢?其实原因就......