插件内部机制¶
许多插件钩子被传递对象,这些对象提供对 Datasette 内部功能的访问。除了此处记录的方法外,这些对象的接口不应被视为稳定。
请求对象¶
请求对象被传递给各种插件钩子。它代表一个传入的 HTTP 请求。它具有以下属性
.scope
- 字典用于构建此请求的 ASGI 范围,如ASGI HTTP 连接范围规范中所述。
.method
- 字符串此请求的 HTTP 方法,通常是
GET
或POST
。.url
- 字符串此请求的完整 URL,例如
https://latest.datasette.io/fixtures
。.scheme
- 字符串请求方案 - 通常是
https
或http
。.headers
- 字典 (str -> str)传入 HTTP 请求头的字典。头名称已转换为小写。
.cookies
- 字典 (str -> str)传入 cookie 的字典
.host
- 字符串传入请求中的主机头,例如
latest.datasette.io
或localhost
。.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-8
、application/json; charset=utf-8
或text/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)
元组列表,可选提供此参数而不是
action
和resource
,以便一次检查多个权限。
此便捷方法可用于回答“此项目是否应被视为私有,即对我可见但对匿名用户不可见?”的问题。
它返回一个包含两个布尔值的元组,(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
将为False
。private
将为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.WARNING
或datasette.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_mutable
和is_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 语句,使用sqlite3
conn.executescript()方法。
await db.execute_write_many(sql, params_seq, block=True)¶
类似于execute_write()
,但使用sqlite3
conn.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 命令连接到此数据库的附加数据库列表。每个命名元组都有字段
seq
、name
和file
。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
(字符串,例如REAL
或VARCHAR(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)
- 字符串 或 Noneawait 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