插件内部机制

许多插件钩子被传递对象,这些对象提供对 Datasette 内部功能的访问。除了此处记录的方法外,这些对象的接口不应被视为稳定。

请求对象

请求对象被传递给各种插件钩子。它代表一个传入的 HTTP 请求。它具有以下属性

.scope - 字典

用于构建此请求的 ASGI 范围,如ASGI HTTP 连接范围规范中所述。

.method - 字符串

此请求的 HTTP 方法,通常是GETPOST

.url - 字符串

此请求的完整 URL,例如https://latest.datasette.io/fixtures

.scheme - 字符串

请求方案 - 通常是httpshttp

.headers - 字典 (str -> str)

传入 HTTP 请求头的字典。头名称已转换为小写。

.cookies - 字典 (str -> str)

传入 cookie 的字典

.host - 字符串

传入请求中的主机头,例如latest.datasette.iolocalhost

.path - 字符串

请求路径,不包括查询字符串,例如/fixtures

.full_path - 字符串

请求路径,如果存在查询字符串则包含查询字符串,例如/fixtures?sql=select+sqlite_version()

.query_string - 字符串

请求的查询字符串组件,不包含? - 例如name__contains=sam&age__gt=10

.args - MultiParams

代表解析后查询字符串参数的对象,见下文。

.url_vars - 字典 (str -> str)

从 URL 路径中提取的变量,如果该路径是使用正则表达式定义的。参见register_routes(datasette)

.actor - 字典 (str -> Any) 或 None

当前认证的参与者(见参与者),如果请求未认证,则为None

该对象还具有两个 awaitable 方法

await request.post_vars() - 字典

返回通过POST提交的请求体中的表单变量字典。别忘了阅读关于CSRF 保护的内容!

await request.post_body() - bytes

返回通过POST提交的未解析的请求体 - 对于传入的 JSON 数据等很有用。

以及一个类方法,可用于创建用于测试的伪造请求对象

fake(path_with_query_string, method="GET", scheme="http", url_vars=None)

返回指定路径和方法的Request实例。例如

from datasette import Request
from pprint import pprint

request = Request.fake(
    "/fixtures/facetable/",
    url_vars={"database": "fixtures", "table": "facetable"},
)
pprint(request.scope)

输出如下

{'http_version': '1.1',
 'method': 'GET',
 'path': '/fixtures/facetable/',
 'query_string': b'',
 'raw_path': b'/fixtures/facetable/',
 'scheme': 'http',
 'type': 'http',
 'url_route': {'kwargs': {'database': 'fixtures', 'table': 'facetable'}}}

MultiParams 类

request.args是一个MultiParams对象 - 一个类似字典的对象,提供对可能具有多个值的查询字符串参数的访问。

考虑查询字符串?foo=1&foo=2&bar=3 - 其中foo有两个值,bar有一个值。

request.args[key] - 字符串

返回该键的第一个值,如果键缺失则引发KeyError。对于上面的例子,request.args["foo"]将返回"1"

request.args.get(key) - 字符串或 None

返回该键的第一个值,如果键缺失则返回None。传入第二个参数以指定不同的默认值,例如q = request.args.get("q", "")

request.args.getlist(key) - 字符串列表

返回该键的字符串列表。request.args.getlist("foo")在上面的例子中将返回["1", "2"]request.args.getlist("bar")将返回["3"]。如果键缺失,将返回一个空列表。

request.args.keys() - 字符串列表

返回可用键列表 - 在示例中这将是["foo", "bar"]

key in request.args - True 或 False

您可以使用if key in request.args检查键是否存在。

for key in request.args - 迭代器

这让您可以遍历所有可用的键。

len(request.args) - 整数

返回键的数量。

响应类

Response类可以从使用register_routes(datasette)钩子注册的视图函数中返回。

Response()构造函数接受以下参数

body - 字符串

响应体。

status - 整数 (可选)

HTTP 状态码 - 默认为 200。

headers - 字典 (可选)

额外 HTTP 头的字典,例如{"x-hello": "world"}

content_type - 字符串 (可选)

响应的内容类型。默认为text/plain

例如

from datasette.utils.asgi import Response

response = Response(
    "<xml>This is XML</xml>",
    content_type="application/xml; charset=utf-8",
)

创建响应最快捷的方法是使用Response.text(...)Response.html(...)Response.json(...)Response.redirect(...)辅助方法

from datasette.utils.asgi import Response

html_response = Response.html("This is HTML")
json_response = Response.json({"this_is": "json"})
text_response = Response.text(
    "This will become utf-8 encoded text"
)
# Redirects are served as 302, unless you pass status=301:
redirect_response = Response.redirect(
    "https://latest.datasette.io/"
)

这些响应中的每一个都将使用相应的正确内容类型 - 分别是text/html; charset=utf-8application/json; charset=utf-8text/plain; charset=utf-8

每个辅助方法都接受可选的status=headers=参数,如上所述。

使用 .asgi_send(send) 返回响应

在大多数情况下,您将从自己的视图函数返回Response对象。您也可以使用Response实例通过 ASGI 以较低层级进行响应,例如,如果您正在编写使用asgi_wrapper(datasette)钩子的代码。

创建一个Response对象,然后使用await response.asgi_send(send),传递 ASGI send函数。例如

async def require_authorization(scope, receive, send):
    response = Response.text(
        "401 Authorization Required",
        headers={
            "www-authenticate": 'Basic realm="Datasette", charset="UTF-8"'
        },
        status=401,
    )
    await response.asgi_send(send)

Datasette 类

此对象是Datasette类的实例,作为名为datasette的参数传递给许多插件钩子。

您可以创建自己的实例 - 例如帮助为插件编写测试 - 如下所示

from datasette.app import Datasette

# With no arguments a single in-memory database will be attached
datasette = Datasette()

# The files= argument can load files from disk
datasette = Datasette(files=["/path/to/my-database.db"])

# Pass metadata as a JSON dictionary like this
datasette = Datasette(
    files=["/path/to/my-database.db"],
    metadata={
        "databases": {
            "my-database": {
                "description": "This is my database"
            }
        }
    },
)

构造函数参数包括

  • files=[...] - 要打开的数据库文件列表

  • immutables=[...] - 要以不可变模式打开的数据库文件列表

  • metadata={...} - 元数据字典

  • config_dir=... - 要使用的配置目录,存储在datasette.config_dir

.databases

属性,公开当前连接到 Datasette 的数据库的collections.OrderedDict

字典键是在 URL 中使用的数据库名称 - 例如,/fixtures的键将是"fixtures"。值是Database 类实例。

列出所有数据库,无论用户权限如何。这意味着_internal数据库将始终在此处列出。

.plugin_config(plugin_name, database=None, table=None)

plugin_name - 字符串

要查找配置的插件名称。通常类似datasette-cluster-map

database - None 或 字符串

用户正在交互的数据库。

table - None 或 字符串

用户正在交互的表。

此方法允许您读取在metadata.json中设置的插件配置值。有关此方法应如何使用的详细信息,请参见编写接受配置的插件

返回值将是配置文件的值 - 通常是一个字典。

如果插件未配置,返回值将是None

await .render_template(template, context=None, request=None)

template - 字符串、字符串列表或 jinja2.Template

要渲染的模板文件,例如my_plugin.html。Datasette 将首先在--template-dir=位置(如果指定)搜索此文件,然后是插件捆绑的模板,最后是 Datasette 的默认模板集。

如果这是一个模板文件名称列表,则将加载并渲染第一个存在的文件。

如果这是一个 Jinja Template 对象,它将被直接使用。

context - None 或 Python 字典

要传递给模板的上下文变量。

request - 请求对象 或 None

如果在此处传递 Datasette 请求对象,它将可供模板使用。

使用 Datasette 预配置的 Jinja 实例渲染Jinja 模板并返回结果字符串。模板将可以访问 Datasette 的默认模板函数以及其他插件提供的任何函数。

await .permission_allowed(actor, action, resource=None, default=False)

actor - 字典

认证的参与者。这通常是request.actor

action - 字符串

正在进行权限检查的操作名称。

resource - 字符串或元组,可选

资源,例如数据库名称,或包含数据库名称和表名称的两个字符串元组。只有某些权限适用于资源。

default - 可选,True 或 False

此权限检查是默认允许还是默认拒绝。

检查给定参与者是否具有对给定资源执行给定操作的权限

一些权限检查是根据metadata.json 中定义的规则执行的,而其他自定义权限可能由实现permission_allowed(datasette, actor, action, resource)插件钩子的插件决定。

如果metadata.json和任何插件都没有为权限查询提供答案,将返回default参数的值。

有关 Datasette 核心中包含的权限操作的完整列表,请参见内置权限

await .ensure_permissions(actor, permissions)

actor - 字典

认证的参与者。这通常是request.actor

permissions - 列表

要检查的权限列表。该列表中的每个权限可以是字符串action名称或(action, resource)的 2 元组。

此方法允许一次检查多个权限。如果任何检查在其中一个被明确授予之前被拒绝,则会引发datasette.Forbidden异常。

当您需要一次检查多个权限时,这很有用。例如,如果以下检查中的任何一个返回True,或者没有任何一个返回False,则参与者应该能够查看表

await self.ds.ensure_permissions(
    request.actor,
    [
        ("view-table", (database, table)),
        ("view-database", database),
        "view-instance",
    ],
)

await .check_visibility(actor, action=None, resource=None, permissions=None)

actor - 字典

认证的参与者。这通常是request.actor

action - 字符串,可选

正在进行权限检查的操作名称。

resource - 字符串或元组,可选

资源,例如数据库名称,或包含数据库名称和表名称的两个字符串元组。只有某些权限适用于资源。

permissions - action字符串列表或(action, resource)元组列表,可选

提供此参数而不是actionresource,以便一次检查多个权限。

此便捷方法可用于回答“此项目是否应被视为私有,即对我可见但对匿名用户不可见?”的问题。

它返回一个包含两个布尔值的元组,(visible, private)visible表示参与者是否可以看到此资源。private将为True,如果匿名用户无法查看该资源。

此示例检查用户是否可以访问特定表,并设置private以便以后可以显示挂锁图标

visible, private = await self.ds.check_visibility(
    request.actor,
    action="view-table",
    resource=(database, table),
)

以下示例连续运行三个检查,类似于await .ensure_permissions(actor, permissions)。如果任何检查在其中一个被明确授予之前被拒绝,则visible将为Falseprivate将为True,如果匿名用户无法查看该资源。

visible, private = await self.ds.check_visibility(
    request.actor,
    permissions=[
        ("view-table", (database, table)),
        ("view-database", database),
        "view-instance",
    ],
)

.get_database(name)

name - 字符串,可选

数据库名称 - 可选。

返回指定的数据库对象。如果数据库不存在,则引发KeyError。不带参数调用此方法将返回第一个连接的数据库。

.add_database(db, name=None, route=None)

db - datasette.database.Database 实例

要附加的数据库。

name - 字符串,可选

用于此数据库的名称。如果未指定,Datasette 将根据文件名或内存名称选择一个。

route - 字符串,可选

这将在 URL 路径中使用。如果未指定,将默认为与name相同。

datasette.add_database(db)方法允许您向当前的 Datasette 实例添加新数据库。

db参数应该是一个datasette.database.Database类的实例。例如

from datasette.database import Database

datasette.add_database(
    Database(
        datasette,
        path="path/to/my-new-database.db",
    )
)

这将添加一个可变数据库,并在/my-new-database提供服务。

使用is_mutable=False添加一个不可变数据库。

.add_database()返回 Database 实例,其名称设置为database.name属性。任何时候处理新添加的数据库时,都应使用.add_database()的返回值,例如

db = datasette.add_database(
    Database(datasette, memory_name="statistics")
)
await db.execute_write(
    "CREATE TABLE foo(id integer primary key)"
)

.add_memory_database(name)

添加一个具有指定名称的共享内存数据库

datasette.add_memory_database("statistics")

这是以下操作的快捷方式

from datasette.database import Database

datasette.add_database(
    Database(datasette, memory_name="statistics")
)

使用这些模式中的任何一种都将导致内存数据库在/statistics提供服务。

.remove_database(name)

name - 字符串

要移除的数据库名称。

这会移除之前添加的数据库。name=是该数据库的唯一名称。

.sign(value, namespace="default")

value - 任何可序列化类型

要签名的值。

namespace - 字符串,可选

备用命名空间,参见itsdangerous salt 文档

用于签名值的实用方法,以便您可以安全地将数据传递到不受信任的环境以及从中获取数据。这是itsdangerous库的包装器。

此方法返回一个签名字符串,可以使用.unsign(value, namespace="default")解码和验证。

.unsign(value, namespace="default")

signed - 任何可序列化类型

使用.sign(value, namespace="default")创建的签名字符串。

namespace - 字符串,可选

如果使用了备用命名空间,则为该命名空间。

返回传递给.sign(value, namespace="default")的原始解码对象。如果签名无效,则会引发itsdangerous.BadSignature异常。

.add_message(request, message, type=datasette.INFO)

request - Request

当前的请求对象

message - 字符串

消息字符串

type - 常量,可选

消息类型 - datasette.INFO, datasette.WARNINGdatasette.ERROR

Datasette 的闪现消息机制允许您添加一条消息,该消息将在用户访问的下一个页面上显示。消息存储在ds_messages cookie 中。此方法将消息添加到该 cookie。

您可以使用/-/messages调试工具尝试这些消息(包括三种消息类型的不同视觉样式)。

.absolute_url(request, path)

request - Request

当前的请求对象

path - 字符串

路径,例如/dbname/table.json

返回给定路径的绝对 URL,包括协议和主机。例如

absolute_url = datasette.absolute_url(
    request, "/dbname/table.json"
)
# Would return "http://localhost:8001/dbname/table.json"

当前请求对象用于确定返回 URL 应使用的主机名和协议。force_https_urls配置设置会被考虑在内。

.setting(key)

key - 字符串

设置名称,例如base_url

返回指定设置的配置值。这可以是字符串、布尔值或整数,具体取决于所请求的设置。

例如

downloads_are_allowed = datasette.setting("allow_download")

datasette.client

插件可以在其运行的 Datasette 实例内部进行模拟的 HTTP 请求。这确保所有 Datasette 的外部 JSON API 对插件也可用,同时避免了访问这些 API 时进行外部 HTTP 调用的开销。

datasette.client对象是HTTPX Python 库的包装器,提供类似于广泛使用的Requests 库的异步友好 API。

它提供以下方法

await datasette.client.get(path, **kwargs) - 返回 HTTPX Response

针对该路径执行内部 GET 请求。

await datasette.client.post(path, **kwargs) - 返回 HTTPX Response

执行内部 POST 请求。使用data={"name": "value"}传递表单参数。

await datasette.client.options(path, **kwargs) - 返回 HTTPX Response

执行内部 OPTIONS 请求。

await datasette.client.head(path, **kwargs) - 返回 HTTPX Response

执行内部 HEAD 请求。

await datasette.client.put(path, **kwargs) - 返回 HTTPX Response

执行内部 PUT 请求。

await datasette.client.patch(path, **kwargs) - 返回 HTTPX Response

执行内部 PATCH 请求。

await datasette.client.delete(path, **kwargs) - 返回 HTTPX Response

执行内部 DELETE 请求。

await datasette.client.request(method, path, **kwargs) - 返回 HTTPX Response

使用给定的 HTTP 方法针对该路径执行内部请求。

这些方法可以与datasette.urls一起使用 - 例如

table_json = (
    await datasette.client.get(
        datasette.urls.table(
            "fixtures", "facetable", format="json"
        )
    )
).json()

datasette.client方法会自动考虑当前的base_url设置,无论您是否使用datasette.urls系列方法来构造路径。

有关可用**kwargs选项和 HTTPX Response 对象结构的文档,请参阅HTTPX 异步文档

datasette.urls

datasette.urls对象包含用于构建 Datasette 内部页面 URL 的方法。插件应使用此方法链接到页面,因为这些方法考虑了可能生效的任何base_url配置设置。

datasette.urls.instance(format=None)

返回 Datasette 实例根页面的 URL。这通常是"/"

datasette.urls.path(path, format=None)

接受一个路径并返回完整路径,考虑了base_url

例如,datasette.urls.path("-/logout")将返回注销页面的路径,默认情况下是"/-/logout",如果base_url设置为/prefix-path/,则将是/prefix-path/-/logout

datasette.urls.logout()

返回注销页面的 URL,通常是"/-/logout"

datasette.urls.static(path)

返回 Datasette 默认静态资产之一的 URL,例如"/-/static/app.css"

datasette.urls.static_plugins(plugin_name, path)

返回属于插件的静态资产之一的 URL。

datasette.urls.static_plugins("datasette_cluster_map", "datasette-cluster-map.js")将返回"/-/static-plugins/datasette_cluster_map/datasette-cluster-map.js"

datasette.urls.static(path)

返回 Datasette 默认静态资产之一的 URL,例如"/-/static/app.css"

datasette.urls.database(database_name, format=None)

返回数据库页面的 URL,例如"/fixtures"

datasette.urls.table(database_name, table_name, format=None)

返回表页面的 URL,例如"/fixtures/facetable"

datasette.urls.query(database_name, query_name, format=None)

返回查询页面的 URL,例如"/fixtures/pragma_cache_size"

这些函数可以通过 Datasette 模板中的{{ urls }}对象访问,例如

<a href="{{ urls.instance() }}">Homepage</a>
<a href="{{ urls.database("fixtures") }}">Fixtures database</a>
<a href="{{ urls.table("fixtures", "facetable") }}">facetable table</a>
<a href="{{ urls.query("fixtures", "pragma_cache_size") }}">pragma_cache_size query</a>

使用format="json"(或"csv"或其他插件支持的格式)参数来获取 JSON 表示的 URL。这是在路径末尾添加了.json

这些方法都返回一个datasette.utils.PrefixedUrlString对象,它是 Python str类型的子类。这使得考虑base_url设置的逻辑能够检测该前缀是否已应用于路径。

Database 类

Database类的实例可用于针对附加的 SQLite 数据库执行查询,并对其模式进行内省。

Database(ds, path=None, is_mutable=True, is_memory=False, memory_name=None)

Database()构造函数可供插件使用,与.add_database(db, name=None, route=None)结合,用于创建和注册新数据库。

参数如下

ds - Datasette 类 (必需)

要将此数据库附加到的 Datasette 实例。

path - 字符串

磁盘上 SQLite 数据库文件的路径。

is_mutable - 布尔值

将其设置为False会导致 Datasette 以不可变模式打开文件。

is_memory - 布尔值

用于创建非共享内存连接。

memory_name - 字符串 或 None

用于创建命名的内存数据库。与常规内存数据库不同,这些数据库可以由多个线程访问,并且对其进行的任何更改将在 Datasette 服务器进程的生命周期内持久存在。

第一个参数是要附加到的datasette实例,第二个是path=,然后is_mutableis_memory都是可选参数。

db.hash

如果数据库以不可变模式打开,此属性返回数据库内容的 64 个字符的 SHA-256 哈希字符串。否则返回None

await db.execute(sql, ...)

执行针对数据库的 SQL 查询并返回结果行(见结果)。

sql - 字符串 (必需)

要执行的 SQL 查询。可以包含?:named参数。

params - 列表或字典

用于参数的值列表或字典。?使用列表,:named使用字典。

truncate - 布尔值

查询返回的行是否应在最大页面大小处截断?默认为True,设置为False以禁用截断。

custom_time_limit - 整数 毫秒

此查询的自定义时间限制。这可以设置为低于 Datasette 配置默认值。如果查询花费的时间超过此限制,它将提前终止并引发dataette.database.QueryInterrupted异常。

page_size - 整数

设置自定义页面大小用于截断,覆盖 Datasette 配置的默认值。

log_sql_errors - 布尔值

除了作为错误引发外,任何 SQL 错误是否应记录到控制台?默认为True

结果

db.execute()方法返回一个Results对象。这可用于访问查询返回的行。

迭代Results对象将生成 SQLite Row 对象。每个对象都可以视为一个元组,或者可以使用row["column"]语法访问

info = []
results = await db.execute("select name from sqlite_master")
for row in results:
    info.append(row["name"])

Results对象还具有以下属性和方法

.truncated - 布尔值

指示此查询是否被截断 - 如果它返回的结果多于指定的page_size。如果为 true,则结果对象将仅提供对查询结果中前page_size行的访问。可以通过将truncate=False传递给db.query()方法来禁用截断。

.columns - 字符串列表

查询返回的列名称列表。

.rows - sqlite3.Row 列表

此属性提供对数据库返回的行列表的直接访问。可以使用results.rows[0]按索引访问特定行。

.first() - 行或 None

返回结果中的第一行,如果没有返回行则返回None

.single_value()

返回结果的第一行的第一列的值 - 但仅限于查询返回单个行且仅包含单个列的情况。否则引发datasette.database.MultipleValues异常。

.__len__()

调用len(results)返回返回结果的(截断后)数量。

await db.execute_fn(fn)

针对在线程中运行的只读数据库连接执行给定的回调函数。函数将传递一个 SQLite 连接,函数的返回值将由await返回。

示例用法

def get_version(conn):
    return conn.execute(
        "select sqlite_version()"
    ).fetchall()[0][0]


version = await db.execute_fn(get_version)

await db.execute_write(sql, params=None, block=True)

SQLite 一次只允许一个数据库连接进行写入。Datasette 为您处理了这个问题,它维护了一个针对给定数据库执行的写入队列。插件可以将写入操作提交到此队列,它们将按照接收到的顺序执行。

此方法可用于将非 SELECT SQL 查询排队,以便在数据库的单个写入连接上执行。

您可以将额外的 SQL 参数作为元组或字典传递。

方法将阻塞直到操作完成,并且返回值将是使用底层sqlite3 Python 库调用conn.execute(...)的返回值。

如果传递block=False,此行为将更改为“发送后即不管” - 查询将被添加到写入队列并在单独的线程中执行,而您的代码可以继续执行其他操作。该方法将返回代表排队任务的 UUID。

await db.execute_write_script(sql, block=True)

类似于execute_write(),但可用于发送通过分号分隔的多个 SQL 语句,使用sqlite3conn.executescript()方法。

await db.execute_write_many(sql, params_seq, block=True)

类似于execute_write(),但使用sqlite3conn.executemany()方法。这将针对params_seq迭代器中的每个参数高效地执行相同的 SQL 语句,例如

await db.execute_write_many(
    "insert into characters (id, name) values (?, ?)",
    [(1, "Melanie"), (2, "Selma"), (2, "Viktor")],
)

await db.execute_write_fn(fn, block=True)

此方法的工作方式类似于.execute_write(),但您可以为其提供一个可调用的 Python 函数,而不是 SQL 语句。您的函数将被排队,并在写入连接可用时被调用,将该连接作为参数传递给函数。

然后,该函数可以执行多个操作,并且在执行期间独占访问单个可写连接,从而保证操作的安全性。

警告

fn需要是一个常规函数,而不是async def函数。

例如

def delete_and_return_count(conn):
    conn.execute("delete from some_table where id > 5")
    return conn.execute(
        "select count(*) from some_table"
    ).fetchone()[0]


try:
    num_rows_left = await database.execute_write_fn(
        delete_and_return_count
    )
except Exception as e:
    print("An error occurred:", e)

await database.execute_write_fn(...)返回的值将是您的函数的返回值。

如果您的函数引发异常,该异常将传播到await行。

如果指定block=False,该方法将变为发送后即不管,将您的函数排队执行,然后允许您在调用.execute_write_fn()后的代码继续运行,而底层线程等待运行您的函数的机会。将返回代表排队任务的 UUID。代码中的任何异常都将被静默吞噬。

db.close()

关闭所有打开的文件支持数据库连接。这主要用于大型测试套件,以避免达到打开文件数量的限制。

数据库内省

Database类还提供用于内省数据库的属性和方法。

db.name - 字符串

数据库名称 - 通常是没有.db前缀的文件名。

db.size - 整数

数据库文件大小(字节)。对于:memory:数据库为 0。

db.mtime_ns - 整数 或 None

数据库文件的最后修改时间(从纪元开始的纳秒)。对于:memory:数据库为None

db.is_mutable - 布尔值

此数据库是否可变,允许写入?

db.is_memory - 布尔值

此数据库是否是内存数据库?

await db.attached_databases() - 命名元组列表

返回已使用 SQLite ATTACH 命令连接到此数据库的附加数据库列表。每个命名元组都有字段seqnamefile

await db.table_exists(table) - 布尔值

检查名为table的表是否存在。

await db.table_names() - 字符串列表

数据库中表的名称列表。

await db.view_names() - 字符串列表

数据库中视图的名称列表。

await db.table_columns(table) - 字符串列表

特定表中的列名称。

await db.table_column_details(table) - 命名元组列表

特定表中列的完整详细信息。每列由一个Column命名元组表示,字段包括cid(表示列位置的整数)、name(字符串)、type(字符串,例如REALVARCHAR(30))、notnull(整数 1 或 0)、default_value(字符串或 None)、is_pk(整数 1 或 0)。

await db.primary_keys(table) - 字符串列表

此表主键中包含的列名称。

await db.fts_table(table) - 字符串 或 None

与此表关联的 FTS 表的名称(如果存在)。

await db.label_column_for_table(table) - 字符串 或 None

与此表关联的标签列 - 自动检测或使用元数据中的"label_column"键,参见为表指定标签列

await db.foreign_keys_for_table(table) - 字典列表

此表中是其他表外键的列的详细信息。一个字典列表,每个字典的格式如下:{"column": 字符串, "other_table": 字符串, "other_column": 字符串}

await db.hidden_table_names() - 字符串列表

Datasette 默认“隐藏”的表列表 - 通常是与 SQLite 全文搜索功能、SpatiaLite 扩展或使用隐藏表功能隐藏的表相关联的表。

await db.get_table_definition(table) - 字符串

返回表的 SQL 定义 - CREATE TABLE语句以及任何相关的CREATE INDEX语句。

await db.get_view_definition(view) - 字符串

返回命名视图的 SQL 定义。

await db.get_all_foreign_keys() - 字典

代表此表的传入和传出外键的字典。它有两个键,"incoming""outgoing",每个键都是一个字典列表,键包括"column""other_table""other_column"。例如

{
    "incoming": [],
    "outgoing": [
        {
            "other_table": "attraction_characteristic",
            "column": "characteristic_id",
            "other_column": "pk",
        },
        {
            "other_table": "roadside_attractions",
            "column": "attraction_id",
            "other_column": "pk",
        }
    ]
}

CSRF 保护

Datasette 使用asgi-csrf来防范针对表单 POST 提交的 CSRF 攻击。用户会收到一个ds_csrftoken cookie,对于每个传入请求,此 cookie 会与csrftoken表单字段(或x-csrftoken HTTP 头)进行比较。

如果您的插件在任何地方实现了<form method="POST">,则需要包含该令牌。您可以使用以下模板片段来实现

<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">

如果您使用await .render_template(template, context=None, request=None)方法渲染模板,则csrftoken()辅助函数仅在您向该方法提供request=参数时起作用。如果您忘记这样做,您将看到以下错误

form-urlencoded POST field did not match cookie

您可以使用skip_csrf(datasette, scope)钩子选择性地禁用 CSRF 保护。

_internal 数据库

警告

此 API 应被视为不稳定 - 这些表的结构在 Datasette 1.0 版本发布之前可能会更改。

Datasette 维护一个内存中的 SQLite 数据库,其中包含所有附加数据库的数据库、表和列的详细信息。

默认情况下,所有参与者都被拒绝访问_internal数据库的view-database权限,因此除非他们以 root 身份登录,否则任何人都无法看到该数据库。

插件可以通过调用db = datasette.get_database("_internal")然后使用Database API执行查询来访问此数据库。

您可以通过以 root 身份登录latest.datasette.io演示实例,然后导航到latest.datasette.io/_internal来探索此数据库的示例。

datasette.utils 模块

datasette.utils模块包含 Datasette 使用的各种实用函数。一般来说,您应该将此模块中的任何内容视为不稳定 - 此处的函数和类可能在 Datasette 发布版本之间更改,恕不另行通知,或完全移除,且不包含在发布说明中。

此规则的例外是此处记录的任何内容。如果您在自己的工作中发现需要使用未记录的实用函数,请考虑提交一个 issue,请求将您使用的函数升级为文档化和受支持的状态。

parse_metadata(content)

此函数接受包含 JSON 或 YAML 的字符串,预期格式如元数据中所述。它返回一个表示从该字符串解析的数据的嵌套 Python 字典。

如果元数据无法解析为 JSON 或 YAML,函数将引发utils.BadMetadataError异常。

datasette.utils.parse_metadata(content: str) dict

检测内容是 JSON 还是 YAML 并进行适当解析。

await_me_maybe(value)

用于在返回值是 awaitable 时对其调用await,否则返回值的实用函数。Datasette 使用此方法来支持可选返回 awaitable 函数的插件钩子。有关此函数的更多信息,请阅读The “await me maybe” pattern for Python asyncio

async datasette.utils.await_me_maybe(value: Any) Any

如果 value 是可调用的,则调用它。如果是 awaitable,则 await 它。否则返回它。

波浪号编码

Datasette 在某些地方使用自定义编码方案,称为波浪号编码(tilde encoding)。这主要用于表名和行主键,以避免这些值中的/字符与引用它们的 Datasette URL 之间的混淆。

波浪号编码使用与URL 百分比编码相同的算法,但使用~波浪号字符代替%

ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz0123456789_-以外的任何字符都将被替换为前缀为波浪号的数字等效字符。例如

  • / 变为 ~2F

  • . 变为 ~2E

  • % 变为 ~25

  • ~ 变为 ~7E

  • 空格变为 +

  • polls/2022.primary 变为 polls~2F2022~2Eprimary

请注意,空格字符是一个特例:它将被替换为+符号。

datasette.utils.tilde_encode(s: str) str

返回波浪号编码的字符串 - 例如/foo/bar -> ~2Ffoo~2Fbar

datasette.utils.tilde_decode(s: str) str

解码波浪号编码的字符串,例如~2Ffoo~2Fbar -> /foo/bar

datasette.tracer

使用--setting trace_debug 1运行 Datasette 会启用跟踪调试输出,然后可以通过在任何页面查询字符串中添加?_trace=1来查看。

您可以在 latest.datasette.io/fixtures/facetable?_trace=1 页面的底部看到一个示例。JSON 输出显示了生成该页面所执行的每个 SQL 查询的完整详细信息。

可以安装 datasette-pretty-traces 插件以提供更易读的信息显示。您可以在这里看到一个演示

您可以使用 trace() 上下文管理器将您自己的自定义追踪添加到 JSON 输出中。它接受一个字符串来标识记录的追踪类型,并将任何关键字参数记录为结果追踪对象上的额外 JSON 键。

追踪执行的开始和结束时间、持续时间以及回溯将自动附加到 JSON 对象中。

此示例使用 trace 来记录使用该函数进行的任何 HTTP GET 请求的开始、结束和持续时间

from datasette.tracer import trace
import httpx


async def fetch_url(url):
    with trace("fetch-url", url=url):
        async with httpx.AsyncClient() as client:
            return await client.get(url)

追踪子任务

如果您的代码使用 asyncio.gather() 等机制在额外的任务中执行代码,您可能会发现显示中缺少一些追踪信息。

您可以使用 trace_child_tasks() 上下文管理器来确保这些子任务得到正确处理。

from datasette import tracer

with tracer.trace_child_tasks():
    results = await asyncio.gather(
        # ... async tasks here
    )

此示例使用 register_routes() 插件钩子在 /parallel-queries 添加了一个页面,该页面使用 asyncio.gather() 并行执行两个 SQL 查询并返回它们的结果。

from datasette import hookimpl
from datasette import tracer


@hookimpl
def register_routes():
    async def parallel_queries(datasette):
        db = datasette.get_database()
        with tracer.trace_child_tasks():
            one, two = await asyncio.gather(
                db.execute("select 1"),
                db.execute("select 2"),
            )
        return Response.json(
            {
                "one": one.single_value(),
                "two": two.single_value(),
            }
        )

    return [
        (r"/parallel-queries$", parallel_queries),
    ]

添加 ?_trace=1 将显示追踪涵盖了这两个子任务。

导入快捷方式

以下常用符号可以直接从 datasette 模块导入

from datasette import Response
from datasette import Forbidden
from datasette import NotFound
from datasette import hookimpl
from datasette import actor_matches_allow