【Python百日进阶-Web开发-Peewee】Day280 - SQLite 扩展(五)

文章目录

      • [12.2.11 class TableFunction](#12.2.11 class TableFunction)
      • [12.2.12 ClosureTable](#12.2.12 ClosureTable)
      • [12.2.13 class BaseClosureTable](#12.2.13 class BaseClosureTable)

12.2.11 class TableFunction

python 复制代码
class TableFunction

实现用户定义的表值函数。与返回单个标量值的简单 标量或聚合函数不同,表值函数可以返回任意数量的表格数据行。

简单的例子:

python 复制代码
from playhouse.sqlite_ext import TableFunction


class Series(TableFunction):
    # Name of columns in each row of generated data.
    columns = ['value']

    # Name of parameters the function may be called with.
    params = ['start', 'stop', 'step']

    def initialize(self, start=0, stop=None, step=1):
        """
        Table-functions declare an initialize() method, which is
        called with whatever arguments the user has called the
        function with.
        """
        self.start = self.current = start
        self.stop = stop or float('Inf')
        self.step = step

    def iterate(self, idx):
        """
        Iterate is called repeatedly by the SQLite database engine
        until the required number of rows has been read **or** the
        function raises a `StopIteration` signalling no more rows
        are available.
        """
        if self.current > self.stop:
            raise StopIteration

        ret, self.current = self.current, self.current + self.step
        return (ret,)

# Register the table-function with our database, which ensures it
# is declared whenever a connection is opened.
db.table_function('series')(Series)

# Usage:
cursor = db.execute_sql('SELECT * FROM series(?, ?, ?)', (0, 5, 2))
for value, in cursor:
    print(value)

笔记

ATableFunction必须先注册到database连接才能使用。为确保表函数始终可用,您可以使用 SqliteDatabase.table_function()装饰器将函数注册到database。

TableFunction实现必须提供两个属性并实现两个方法,如下所述。

columns

一个列表,其中包含函数返回的数据的列名。例如,用于在分隔符上拆分字符串的函数可能指定 3 列:.substring, start_idx, end_idx

params

可以调用函数的参数的名称。应列出所有参数,包括可选参数。例如,用于在分隔符上拆分字符串的函数可能指定 2 个参数:。string, delimiter

name

可选- 指定表函数的名称。如果未提供,名称将取自类名。

print_tracebacks = True

打印表函数回调方法中发生的任何错误的完整回溯。当设置为 False 时,只有通用 OperationalError 可见。

initialize( **parameter_values )

参数: 参数值-- 调用函数的参数。

返回: 没有返回值。

调用该initialize方法以使用用户在调用函数时指定的参数初始化表函数。

iterate( idx )

参数: 编号( int ) - 当前迭代步骤

返回: 对应于columns属性中命名的列的行数据元组。

提高: 停止迭代-- 表示没有更多行可用。

该函数被重复调用并返回连续的数据行。该函数可能会在所有行被消耗之前终止(特别是如果用户LIMIT在结果上指定了 a )。或者,该函数可以通过引发 StopIteration异常来表示没有更多数据可用。

classname register(conn)

参数: conn-- 一个sqlite3.Connection对象。

sqlite3.Connection 使用 DB-API 2.0对象注册表函数。表值函数必须先注册,然后才能在查询中使用。

例子:

python 复制代码
class MyTableFunction(TableFunction):
    name = 'my_func'
    # ... other attributes and methods ...

db = SqliteDatabase(':memory:')
db.connect()

MyTableFunction.register(db.connection())

为了确保TableFunction每次打开连接时都注册,请使用table_function() 装饰器。

12.2.12 ClosureTable

python 复制代码
ClosureTable(model_class[, foreign_key=None[, referencing_class=None[, referencing_key=None]]])

参数:

  • model_class -- 包含树中节点的模型类。
  • foreign_key -- 模型类上的自引用父节点字段。如果未提供,peewee 将自省模型以找到合适的密钥。
  • referencing_class -- 多对多关系的中间表。
  • referenceencing_key -- 对于多对多关系,关系的发起方。
    返回:
    返回一个VirtualModel用于处理闭包表。

用于创建适用于 传递闭包 表的模型类的工厂函数。闭包表是VirtualModel与传递闭包 SQLite 扩展一起使用的子类。这些特殊表旨在使高效查询分层数据变得容易。SQLite 扩展在幕后管理 AVL 树,当您的表发生更改时透明地更新树,并使对分层数据执行常见查询变得容易。

要在项目中使用闭包表扩展,您需要:

SQLite 扩展的副本。可以在SQLite 代码存储库中找到源代码,也可以 通过克隆以下 gist找到源代码:

python 复制代码
$ git clone https://gist.github.com/coleifer/7f3593c5c2a645913b92 closure
$ cd closure/

将扩展编译为共享库,例如

python 复制代码
$ gcc -g -fPIC -shared closure.c -o closure.so

为您的分层数据创建模型。这里唯一的要求是模型有一个整数主键和一个自引用外键。任何其他字段都可以。

python 复制代码
class Category(Model):
    name = CharField()
    metadata = TextField()
    parent = ForeignKeyField('self', index=True, null=True)  # Required.

# Generate a model for the closure virtual table.
CategoryClosure = ClosureTable(Category)

自引用也可以通过中间表(对于多对多关系)来实现。

python 复制代码
class User(Model):
    name = CharField()

class UserRelations(Model):
    user = ForeignKeyField(User)
    knows = ForeignKeyField(User, backref='_known_by')

    class Meta:
        primary_key = CompositeKey('user', 'knows') # Alternatively, a unique index on both columns.

# Generate a model for the closure virtual table, specifying the UserRelations as the referencing table
UserClosure = ClosureTable(
    User,
    referencing_class=UserRelations,
    foreign_key=UserRelations.knows,
    referencing_key=UserRelations.user)

在您的应用程序代码中,确保在实例化Database对象时加载扩展。这是通过将共享库的路径传递给load_extension()方法来完成的。

python 复制代码
db = SqliteExtDatabase('my_database.db')
db.load_extension('/path/to/closure')

警告

transitive_closure使用扩展程序时,您应该注意两个警告 。首先,它要求您的源模型具有整数主键。其次,强烈建议您在自引用外键上创建索引。

例子:

python 复制代码
class Category(Model):
    name = CharField()
    metadata = TextField()
    parent = ForeignKeyField('self', index=True, null=True)  # Required.

# Generate a model for the closure virtual table.
CategoryClosure = ClosureTable(Category)

 # Create the tables if they do not exist.
 db.create_tables([Category, CategoryClosure], True)

现在可以使用闭包表中的数据执行有趣的查询:

python 复制代码
# Get all ancestors for a particular node.
laptops = Category.get(Category.name == 'Laptops')
for parent in Closure.ancestors(laptops):
    print(parent.name)

# Computer Hardware
# Computers
# Electronics
# All products

# Get all descendants for a particular node.
hardware = Category.get(Category.name == 'Computer Hardware')
for node in Closure.descendants(hardware):
    print(node.name)

# Laptops
# Desktops
# Hard-drives
# Monitors
# LCD Monitors
# LED Monitors

由 VirtualModel.返回的API ClosureTable()。

12.2.13 class BaseClosureTable

python 复制代码
class BaseClosureTable

id

给定节点的主键字段。

depth

表示给定节点的相对深度的字段。

root

表示相对根节点的字段。

descendants(node, depth=None\[, include_node=False])

检索给定节点的所有后代。如果指定了深度,则仅返回该深度(相对于给定节点)的节点。

python 复制代码
node = Category.get(Category.name == 'Electronics')

# Direct child categories.
children = CategoryClosure.descendants(node, depth=1)

# Grand-child categories.
children = CategoryClosure.descendants(node, depth=2)

# Descendants at all depths.
all_descendants = CategoryClosure.descendants(node)

ancestors(node, depth=None\[, include_node=False])

检索给定节点的所有祖先。如果指定了深度,则仅返回该深度(相对于给定节点)的节点。

python 复制代码
node = Category.get(Category.name == 'Laptops')

# All ancestors.
all_ancestors = CategoryClosure.ancestors(node)

# Grand-parent category.
grandparent = CategoryClosure.ancestores(node, depth=2)

siblings(node, include_node=False)

检索作为指定节点父节点的子节点的所有节点。

笔记

有关 SQLite 传递闭包扩展的深入讨论,请查看这篇博文,使用 Python 和传递闭包扩展查询 SQLite 中的树结构。

相关推荐
miss22 分钟前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan1 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真1 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment1 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan1 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么1 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技2 小时前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
合尘猫2 小时前
图片表格变结构化数据:OCR 后的三个坑
python·ocr
小小张说故事2 小时前
Python 里弱引用到底弱在哪:拆开 weakref.ref、回调陷阱和 WeakValueDictionary
python
西安小哥2 小时前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能