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

本文介绍了如何在PeeweeORM中使用TableFunction实现用户定义的表值函数,ClosureTable用于处理闭包表以高效查询分层数据,以及BaseClosureTable的基本操作,如查询祖先和后代。着重讲解了SQLite扩展在管理闭包表中的角色和注意事项。
摘要由CSDN通过智能技术生成

12.2.11 class TableFunction

class TableFunction

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

简单的例子:

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对象注册表函数。表值函数必须先注册,然后才能在查询中使用。

例子:

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

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找到源代码:

$ git clone https://gist.github.com/coleifer/7f3593c5c2a645913b92 closure
$ cd closure/

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

$ gcc -g -fPIC -shared closure.c -o closure.so

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

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)

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

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()方法来完成的。

db = SqliteExtDatabase('my_database.db')
db.load_extension('/path/to/closure')

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

例子:

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)

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

# 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

class BaseClosureTable

id

给定节点的主键字段。

depth

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

root

表示相对根节点的字段。

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

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

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]])

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

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 中的树结构。

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用 FastAPI 中 Tortoise-ORM、SQLAlchemy 和 peewee 进行 ORM 查询的示例。 ## Tortoise-ORM ```python from fastapi import FastAPI from tortoise import fields from tortoise.contrib.fastapi import register_tortoise, HTTPNotFoundError from tortoise.models import Model from tortoise import Tortoise class User(Model): id = fields.IntField(pk=True) name = fields.CharField(50) email = fields.CharField(50) class Meta: table = "users" app = FastAPI() @app.on_event("startup") async def startup(): await Tortoise.init( db_url="sqlite://db.sqlite3", modules={"models": ["main"]} ) await Tortoise.generate_schemas() @app.on_event("shutdown") async def shutdown(): await Tortoise.close_connections() @app.get("/users") async def get_users(): users = await User.all() return users @app.get("/users/{user_id}") async def get_user(user_id: int): user = await User.get_or_none(id=user_id) if user is None: raise HTTPNotFoundError return user register_tortoise( app, db_url="sqlite://db.sqlite3", modules={"models": ["main"]}, generate_schemas=True, add_exception_handlers=True ) ``` ## SQLAlchemy ```python from fastapi import FastAPI from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) name = Column(String(50)) email = Column(String(50)) engine = create_engine("sqlite:///db.sqlite3") SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) app = FastAPI() @app.get("/users") async def get_users(): db = SessionLocal() users = db.query(User).all() db.close() return users @app.get("/users/{user_id}") async def get_user(user_id: int): db = SessionLocal() user = db.query(User).filter(User.id == user_id).first() db.close() if user is None: raise HTTPNotFoundError return user ``` ## peewee ```python from fastapi import FastAPI from peewee import SqliteDatabase, Model, CharField, IntegerField from playhouse.shortcuts import model_to_dict db = SqliteDatabase("db.sqlite3") class User(Model): id = IntegerField(primary_key=True) name = CharField() email = CharField() class Meta: database = db table_name = "users" app = FastAPI() @app.on_event("startup") def startup(): db.connect() db.create_tables([User]) @app.on_event("shutdown") def shutdown(): db.close() @app.get("/users") async def get_users(): users = [model_to_dict(user) for user in User.select()] return users @app.get("/users/{user_id}") async def get_user(user_id: int): user = User.get_or_none(User.id == user_id) if user is None: raise HTTPNotFoundError return model_to_dict(user) ``` 注意:以上示例中的代码仅用于演示 ORM 查询的基本用法,并且没有进行错误处理。在实际应用中,你应该根据需要添加适当的错误处理和安全性检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岳涛@心馨电脑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值