文章目录
-
-
- [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 中的树结构。