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["escape_css_string"] = escape_css_string
 | 
				
			||||||
        self.jinja_env.filters["quote_plus"] = lambda u: urllib.parse.quote_plus(u)
 | 
					        self.jinja_env.filters["quote_plus"] = lambda u: urllib.parse.quote_plus(u)
 | 
				
			||||||
        self.jinja_env.filters["escape_sqlite"] = escape_sqlite
 | 
					        self.jinja_env.filters["escape_sqlite"] = escape_sqlite
 | 
				
			||||||
| 
						 | 
					@ -730,5 +732,5 @@ class DatasetteRouter(AsgiRouter):
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            template = self.ds.jinja_env.select_template(templates)
 | 
					            template = self.ds.jinja_env.select_template(templates)
 | 
				
			||||||
            await asgi_send_html(
 | 
					            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)
 | 
					            extra_template_vars.update(extra_vars)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Response.html(
 | 
					        return Response.html(
 | 
				
			||||||
            template.render(
 | 
					            await template.render_async(
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    **context,
 | 
					                    **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.
 | 
					    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
 | 
					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:
 | 
					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")
 | 
					            "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
 | 
					.. code-block:: python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @hookimpl
 | 
					    @hookimpl
 | 
				
			||||||
    def extra_template_vars(datasette):
 | 
					    def extra_template_vars(datasette, database):
 | 
				
			||||||
        async def inner():
 | 
					        async def sql_first(sql, dbname=None):
 | 
				
			||||||
            first_db = list(datasette.databases.keys())[0]
 | 
					            dbname = dbname or database or next(iter(datasette.databases.keys()))
 | 
				
			||||||
            return {
 | 
					            return (await datasette.execute(dbname, sql)).rows[0][0]
 | 
				
			||||||
                "sqlite_version": (
 | 
					
 | 
				
			||||||
                    await datasette.execute(first_db, "select sqlite_version()")
 | 
					You can then use the new function in a template like so::
 | 
				
			||||||
                ).rows[0][0]
 | 
					
 | 
				
			||||||
            }
 | 
					    SQLite version: {{ sql_first("select sqlite_version()") }}
 | 
				
			||||||
        return inner
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. _plugin_register_output_renderer:
 | 
					.. _plugin_register_output_renderer:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -446,13 +446,19 @@ def render_cell(value, database):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@hookimpl
 | 
					@hookimpl
 | 
				
			||||||
def extra_template_vars(template, database, table, view_name, request, datasette):
 | 
					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():
 | 
					    async def inner():
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            "extra_template_vars_from_awaitable": json.dumps({
 | 
					            "extra_template_vars_from_awaitable": json.dumps({
 | 
				
			||||||
                "template": template,
 | 
					                "template": template,
 | 
				
			||||||
                "scope_path": request.scope["path"],
 | 
					                "scope_path": request.scope["path"],
 | 
				
			||||||
                "awaitable": True,
 | 
					                "awaitable": True,
 | 
				
			||||||
            }, default=lambda b: b.decode("utf8"))
 | 
					            }, default=lambda b: b.decode("utf8")),
 | 
				
			||||||
 | 
					            "query_database": query_database,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    return inner
 | 
					    return inner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
from bs4 import BeautifulSoup as Soup
 | 
					from bs4 import BeautifulSoup as Soup
 | 
				
			||||||
from .fixtures import app_client, make_app_client, TEMP_PLUGIN_SECRET_FILE  # noqa
 | 
					from .fixtures import app_client, make_app_client, TEMP_PLUGIN_SECRET_FILE  # noqa
 | 
				
			||||||
 | 
					from datasette.utils import sqlite3
 | 
				
			||||||
import base64
 | 
					import base64
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
| 
						 | 
					@ -214,3 +215,20 @@ def test_plugins_extra_template_vars(restore_working_directory):
 | 
				
			||||||
            "awaitable": True,
 | 
					            "awaitable": True,
 | 
				
			||||||
            "scope_path": "/-/metadata",
 | 
					            "scope_path": "/-/metadata",
 | 
				
			||||||
        } == extra_template_vars_from_awaitable
 | 
					        } == 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:
 | 
					Test data for extra_template_vars:
 | 
				
			||||||
<pre class="extra_template_vars">{{ extra_template_vars|safe }}</pre>
 | 
					<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_template_vars_from_awaitable">{{ extra_template_vars_from_awaitable|safe }}</pre>
 | 
				
			||||||
 | 
					<pre class="extra_from_awaitable_function">{{ query_database("select sqlite_version();") }}</pre>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Ładowanie…
	
		Reference in New Issue