kopia lustrzana https://github.com/simonw/datasette
rodzic
b51f258d00
commit
8c642f04e0
|
@ -583,7 +583,9 @@ class Datasette:
|
|||
),
|
||||
]
|
||||
)
|
||||
self.jinja_env = Environment(loader=template_loader, autoescape=True)
|
||||
self.jinja_env = Environment(
|
||||
loader=template_loader, autoescape=True, enable_async=True
|
||||
)
|
||||
self.jinja_env.filters["escape_css_string"] = escape_css_string
|
||||
self.jinja_env.filters["quote_plus"] = lambda u: urllib.parse.quote_plus(u)
|
||||
self.jinja_env.filters["escape_sqlite"] = escape_sqlite
|
||||
|
@ -730,5 +732,5 @@ class DatasetteRouter(AsgiRouter):
|
|||
else:
|
||||
template = self.ds.jinja_env.select_template(templates)
|
||||
await asgi_send_html(
|
||||
send, template.render(info), status=status, headers=headers
|
||||
send, await template.render_async(info), status=status, headers=headers
|
||||
)
|
||||
|
|
|
@ -139,7 +139,7 @@ class BaseView(AsgiView):
|
|||
extra_template_vars.update(extra_vars)
|
||||
|
||||
return Response.html(
|
||||
template.render(
|
||||
await template.render_async(
|
||||
{
|
||||
**context,
|
||||
**{
|
||||
|
|
|
@ -629,7 +629,9 @@ Function that returns a dictionary
|
|||
If you return a function it will be executed. If it returns a dictionary those values will will be merged into the template context.
|
||||
|
||||
Function that returns an awaitable function that returns a dictionary
|
||||
You can also return a function which returns an awaitable function which returns a dictionary. This means you can execute additional SQL queries using ``datasette.execute()``.
|
||||
You can also return a function which returns an awaitable function which returns a dictionary.
|
||||
|
||||
Datasette runs Jinja2 in `async mode <https://jinja.palletsprojects.com/en/2.10.x/api/#async-support>`__, which means you can add awaitable functions to the template scope and they will be automatically awaited when they are rendered by the template.
|
||||
|
||||
Here's an example plugin that returns an authentication object from the ASGI scope:
|
||||
|
||||
|
@ -641,20 +643,19 @@ Here's an example plugin that returns an authentication object from the ASGI sco
|
|||
"auth": request.scope.get("auth")
|
||||
}
|
||||
|
||||
And here's an example which returns the current version of SQLite:
|
||||
And here's an example which adds a ``sql_first(sql_query)`` function which executes a SQL statement and returns the first column of the first row of results:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@hookimpl
|
||||
def extra_template_vars(datasette):
|
||||
async def inner():
|
||||
first_db = list(datasette.databases.keys())[0]
|
||||
return {
|
||||
"sqlite_version": (
|
||||
await datasette.execute(first_db, "select sqlite_version()")
|
||||
).rows[0][0]
|
||||
}
|
||||
return inner
|
||||
def extra_template_vars(datasette, database):
|
||||
async def sql_first(sql, dbname=None):
|
||||
dbname = dbname or database or next(iter(datasette.databases.keys()))
|
||||
return (await datasette.execute(dbname, sql)).rows[0][0]
|
||||
|
||||
You can then use the new function in a template like so::
|
||||
|
||||
SQLite version: {{ sql_first("select sqlite_version()") }}
|
||||
|
||||
.. _plugin_register_output_renderer:
|
||||
|
||||
|
|
|
@ -446,13 +446,19 @@ def render_cell(value, database):
|
|||
|
||||
@hookimpl
|
||||
def extra_template_vars(template, database, table, view_name, request, datasette):
|
||||
async def query_database(sql):
|
||||
first_db = list(datasette.databases.keys())[0]
|
||||
return (
|
||||
await datasette.execute(first_db, sql)
|
||||
).rows[0][0]
|
||||
async def inner():
|
||||
return {
|
||||
"extra_template_vars_from_awaitable": json.dumps({
|
||||
"template": template,
|
||||
"scope_path": request.scope["path"],
|
||||
"awaitable": True,
|
||||
}, default=lambda b: b.decode("utf8"))
|
||||
}, default=lambda b: b.decode("utf8")),
|
||||
"query_database": query_database,
|
||||
}
|
||||
return inner
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from bs4 import BeautifulSoup as Soup
|
||||
from .fixtures import app_client, make_app_client, TEMP_PLUGIN_SECRET_FILE # noqa
|
||||
from datasette.utils import sqlite3
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
|
@ -214,3 +215,20 @@ def test_plugins_extra_template_vars(restore_working_directory):
|
|||
"awaitable": True,
|
||||
"scope_path": "/-/metadata",
|
||||
} == extra_template_vars_from_awaitable
|
||||
|
||||
|
||||
def test_plugins_async_template_function(restore_working_directory):
|
||||
for client in make_app_client(
|
||||
template_dir=str(pathlib.Path(__file__).parent / "test_templates")
|
||||
):
|
||||
response = client.get("/-/metadata")
|
||||
assert response.status == 200
|
||||
extra_from_awaitable_function = (
|
||||
Soup(response.body, "html.parser")
|
||||
.select("pre.extra_from_awaitable_function")[0]
|
||||
.text
|
||||
)
|
||||
expected = (
|
||||
sqlite3.connect(":memory:").execute("select sqlite_version()").fetchone()[0]
|
||||
)
|
||||
assert expected == extra_from_awaitable_function
|
||||
|
|
|
@ -5,4 +5,5 @@
|
|||
Test data for extra_template_vars:
|
||||
<pre class="extra_template_vars">{{ extra_template_vars|safe }}</pre>
|
||||
<pre class="extra_template_vars_from_awaitable">{{ extra_template_vars_from_awaitable|safe }}</pre>
|
||||
<pre class="extra_from_awaitable_function">{{ query_database("select sqlite_version();") }}</pre>
|
||||
{% endblock %}
|
||||
|
|
Ładowanie…
Reference in New Issue