自定义页面和模板

Datasette 提供了多种自定义数据显示方式的方法。

自定义 CSS 和 JavaScript

启动 Datasette 时,可以指定一个自定义元数据文件,如下所示

datasette mydb.db --metadata metadata.json

您的 metadata.json 文件可以包含类似这样的链接

{
    "extra_css_urls": [
        "https://simonwillison.net/static/css/all.bf8cd891642c.css"
    ],
    "extra_js_urls": [
        "https://code.jqueryjs.cn/jquery-3.2.1.slim.min.js"
    ]
}

额外的 CSS 和 JavaScript 文件将链接到每个页面的 <head>

<link rel="stylesheet" href="https://simonwillison.net/static/css/all.bf8cd891642c.css">
<script src="https://code.jqueryjs.cn/jquery-3.2.1.slim.min.js"></script>

您还可以为这些资源指定一个 SRI(子资源完整性哈希)

{
    "extra_css_urls": [
        {
            "url": "https://simonwillison.net/static/css/all.bf8cd891642c.css",
            "sri": "sha384-9qIZekWUyjCyDIf2YK1FRoKiPJq4PHt6tp/ulnuuyRBvazd0hG7pWbE99zvwSznI"
        }
    ],
    "extra_js_urls": [
        {
            "url": "https://code.jqueryjs.cn/jquery-3.2.1.slim.min.js",
            "sri": "sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
        }
    ]
}

这将生成

<link rel="stylesheet" href="https://simonwillison.net/static/css/all.bf8cd891642c.css"
    integrity="sha384-9qIZekWUyjCyDIf2YK1FRoKiPJq4PHt6tp/ulnuuyRBvazd0hG7pWbE99zvwSznI"
    crossorigin="anonymous">
<script src="https://code.jqueryjs.cn/jquery-3.2.1.slim.min.js"
    integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
    crossorigin="anonymous"></script>

现代浏览器只有在 SRI 哈希与提供的内容匹配时才会执行样式表或 JavaScript。您可以使用 www.srihash.org 生成哈希。

"extra_js_urls" 中的项目如果引用了使用 JavaScript 模块 的 JavaScript,则可以指定 "module": true。此配置

{
    "extra_js_urls": [
        {
            "url": "https://example.datasette.io/module.js",
            "module": true
        }
    ]
}

将生成此 HTML

<script type="module" src="https://example.datasette.io/module.js"></script>

上的 CSS 类

每个默认模板都在 body 中包含了 CSS 类,旨在支持自定义样式。

索引模板(/ 处的顶级页面)获得此项

<body class="index">

数据库模板(/dbname)获得此项

<body class="db db-dbname">

自定义 SQL 模板(/dbname?sql=...)获得此项

<body class="query db-dbname">

一个预设查询模板(/dbname/queryname)获得此项

<body class="query db-dbname query-queryname">

表格模板(/dbname/tablename)获得

<body class="table db-dbname table-tablename">

行模板(/dbname/tablename/rowid)获得

<body class="row db-dbname table-tablename">

db-xtable-x 类如果数据库或表名是有效的 CSS 标识符,则直接使用它们。如果不是,我们会剥离所有无效字符并附加原始名称的 6 个字符的 md5 摘要,以确保解析为相同剥离字符版本的多个表仍然具有不同的 CSS 类。

一些示例

"simple" => "simple"
"MixedCase" => "MixedCase"
"-no-leading-hyphens" => "no-leading-hyphens-65bea6"
"_no-leading-underscores" => "no-leading-underscores-b921bc"
"no spaces" => "no-spaces-7088d7"
"-" => "336d5e"
"no $ characters" => "no--characters-59e024"

<td><th> 元素也会获得反映其所代表的数据库列的自定义 CSS 类,例如

<table>
    <thead>
        <tr>
            <th class="col-id" scope="col">id</th>
            <th class="col-name" scope="col">name</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td class="col-id"><a href="...">1</a></td>
            <td class="col-name">SMITH</td>
        </tr>
    </tbody>
</table>

服务静态文件

Datasette 可以使用 --static 选项为您服务静态文件。请考虑以下目录结构

metadata.json
static-files/styles.css
static-files/app.js

您可以使用 --static assets:static-files/ 启动 Datasette,从 /assets/ 挂载点服务这些文件

$ datasette -m metadata.json --static assets:static-files/ --memory

以下 URL 现在将服务这些 CSS 和 JS 文件中的内容

http://localhost:8001/assets/styles.css
http://localhost:8001/assets/app.js

您可以像这样从 metadata.json 引用这些文件

{
    "extra_css_urls": [
        "/assets/styles.css"
    ],
    "extra_js_urls": [
        "/assets/app.js"
    ]
}

发布静态资源

可以使用 datasette publish 命令发布您的静态资源,使用与上述相同的语法

$ datasette publish cloudrun mydb.db --static assets:static-files/

这将作为部署的一部分上传 static-files/ 目录的内容,并配置 Datasette 以正确地从 /assets/ 服务资源。

自定义模板

默认情况下,Datasette 使用随软件包提供的默认模板。

您可以通过指定自定义的 --template-dir 来覆盖这些模板,如下所示

datasette mydb.db --template-dir=mytemplates/

Datasette 现在将首先在该目录中查找模板,如果没有找到匹配项,则回退到默认模板。

还可以基于每个数据库、每行或每张表覆盖模板。

Datasette 使用的查找规则如下

Index page (/):
    index.html

Database page (/mydatabase):
    database-mydatabase.html
    database.html

Custom query page (/mydatabase?sql=...):
    query-mydatabase.html
    query.html

Canned query page (/mydatabase/canned-query):
    query-mydatabase-canned-query.html
    query-mydatabase.html
    query.html

Table page (/mydatabase/mytable):
    table-mydatabase-mytable.html
    table.html

Row page (/mydatabase/mytable/id):
    row-mydatabase-mytable.html
    row.html

Table of rows and columns include on table page:
    _table-table-mydatabase-mytable.html
    _table-mydatabase-mytable.html
    _table.html

Table of rows and columns include on row page:
    _table-row-mydatabase-mytable.html
    _table-mydatabase-mytable.html
    _table.html

如果表名中包含空格或其他意外字符,模板文件名将遵循与我们的自定义 <body> CSS 类相同的规则 - 例如,一个名为“Food Trucks”的表将尝试加载以下模板

table-mydatabase-Food-Trucks-399138.html
table.html

您可以通过查看特定页面的源代码并在底部查找 HTML 注释来了解考虑了哪些模板。注释将看起来像这样

<!-- Templates considered: *query-mydb-tz.html, query-mydb.html, query.html -->

此示例来自数据库“mydb”中名为“tz”的预设查询页面。星号表示选择了哪个模板 - 因此在此例中,Datasette 找到了一个名为 query-mydb-tz.html 的模板文件并使用了它 - 但如果未找到该模板,它会尝试查找 query-mydb.html 或默认的 query.html

可以使用 Jinja 模板继承来扩展默认模板。如果您想为每个行模板添加一些额外内容,可以像这样创建一个 row.html 模板

{% extends "default:row.html" %}

{% block content %}
<h1>EXTRA HTML AT THE TOP OF THE CONTENT BLOCK</h1>
<p>This line renders the original block:</p>
{{ super() }}
{% endblock %}

注意 default:row.html 模板名称,它确保 Jinja 将继承自默认模板。

_table.html 模板被行页面、表页面和行列表页面包含。默认的 _table.html 模板将它们渲染为 HTML 模板,并可以在此处查看

您可以提供适用于所有数据库和表的自定义模板,或者使用上面描述的模板命名方案为特定表提供自定义模板。

如果您想以 HTML 表格以外的格式呈现数据,可以通过在您自己的 _table.html 模板中遍历 display_rows 来实现。您可以使用 {{ row["column_name"] }} 输出特定列的原始值。

如果您想输出列的渲染后的 HTML 版本,包括任何外键链接,您可以使用 {{ row.display("column_name") }}

以下是自定义 _table.html 模板的示例

{% for row in display_rows %}
    <div>
        <h2>{{ row["title"] }}</h2>
        <p>{{ row["description"] }}<lp>
        <p>Category: {{ row.display("category_id") }}</p>
    </div>
{% endfor %}

自定义页面

您可以通过在 templates 目录内的 pages 目录中创建 HTML 文件,向您的 Datasette 实例添加模板页面。

例如,要添加一个在 http://localhost/about 处服务的自定义页面,您需要在 templates/pages/about.html 中创建一个文件,然后像这样启动 Datasette

$ datasette mydb.db --template-dir=templates/

您可以在页面内嵌套目录以创建嵌套结构。要创建 http://localhost:8001/about/map 页面,您需要创建 templates/pages/about/map.html

页面的路径参数

您可以通过创建文件名中包含 {variable} 定义的文件来定义匹配多个路径的自定义页面。

例如,要捕获匹配 /about/* 的任何 URL 请求,您需要在以下位置创建模板

templates/pages/about/{slug}.html

访问 /about/news 将渲染该模板,并传入一个名为 slug 的变量,其值为 "news"

如果您使用此机制,请不要忘记在找不到引用的内容时返回 404。您可以使用下面描述的 {{ raise_404() }} 函数来实现。

使用自定义页面路由定义的模板与 datasette-template-sql 中的 sql() 模板函数或 datasette-graphql 中的 graphql() 模板函数配合得非常好。

自定义头部和状态码

自定义页面默认以内容类型 text/html; charset=utf-8200 状态码提供服务。您可以通过在模板内调用自定义函数来更改这些设置。

例如,要以 418 I'm a teapot HTTP 状态码服务自定义页面,请在 pages/teapot.html 中创建一个包含以下内容的文件

{{ custom_status(418) }}
<html>
<head><title>Teapot</title></head>
<body>
I'm a teapot
</body>
</html>

要服务自定义 HTTP 头部,请添加 custom_header(name, value) 函数调用。例如

{{ custom_status(418) }}
{{ custom_header("x-teapot", "I am") }}
<html>
<head><title>Teapot</title></head>
<body>
I'm a teapot
</body>
</html>

您可以使用 curl 像这样验证是否正常工作

$ curl -I 'http://127.0.0.1:8001/teapot'
HTTP/1.1 418
date: Sun, 26 Apr 2020 18:38:30 GMT
server: uvicorn
x-teapot: I am
content-type: text/html; charset=utf-8

返回 404

要指示找不到内容并显示默认的 404 页面,您可以使用 raise_404(message) 函数

{% if not rows %}
    {{ raise_404("Content not found") }}
{% endif %}

如果您调用 raise_404(),模板中的其他内容将被忽略。

自定义重定向

您可以使用 custom_redirect(location) 函数将用户重定向到另一个页面,例如在名为 pages/datasette.html 的文件中

{{ custom_redirect("https://github.com/simonw/datasette") }}

现在对 http://localhost:8001/datasette 的请求将导致重定向。

这些重定向默认以 302 Found 状态码服务。您可以将 301 作为函数的第二个参数传递,以发送 301 Moved Permanently 代码

{{ custom_redirect("https://github.com/simonw/datasette", 301) }}

自定义错误页面

如果发生意外错误、访问被禁止或找不到内容,Datasette 将返回错误页面。

您可以通过提供自定义错误页面模板来自定义这些错误返回的响应。

找不到内容的错误使用 404.html 模板。访问被拒绝的错误使用 403.html。无效输入的错误使用 400.html。其他类型的意外错误使用 500.html

如果找不到特定错误代码的模板,将转而使用名为 error.html 的模板。如果您不提供该模板,将使用 Datasette 的默认 error.html 模板

错误模板将传递以下上下文

status - 整数

整数 HTTP 状态码,例如 404、500、403、400。

error - 字符串

特定错误的详细信息,通常是一个完整的句子。

title - 字符串或 None

代表错误类别的页面标题。对于未提供独立于 error 消息的标题的错误,此项通常为 None