.. _plugin_hooks: Plugin hooks ============ Datasette :ref:`plugins ` use *plugin hooks* to customize Datasette's behavior. These hooks are powered by the `pluggy `__ plugin system. Each plugin can implement one or more hooks using the ``@hookimpl`` decorator against a function named that matches one of the hooks documented on this page. When you implement a plugin hook you can accept any or all of the parameters that are documented as being passed to that hook. For example, you can implement the ``render_cell`` plugin hook like this even though the full documented hook signature is ``render_cell(value, column, table, database, datasette)``: .. code-block:: python @hookimpl def render_cell(value, column): if column == "stars": return "*" * int(value) .. contents:: List of plugin hooks :local: .. _plugin_hook_prepare_connection: prepare_connection(conn, database, datasette) --------------------------------------------- ``conn`` - sqlite3 connection object The connection that is being opened ``database`` - string The name of the database ``datasette`` - :ref:`internals_datasette` You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)`` This hook is called when a new SQLite database connection is created. You can use it to `register custom SQL functions `_, aggregates and collations. For example: .. code-block:: python from datasette import hookimpl import random @hookimpl def prepare_connection(conn): conn.create_function('random_integer', 2, random.randint) This registers a SQL function called ``random_integer`` which takes two arguments and can be called like this:: select random_integer(1, 10); Examples: `datasette-jellyfish `__, `datasette-jq `__, `datasette-haversine `__, `datasette-rure `__ .. _plugin_hook_prepare_jinja2_environment: prepare_jinja2_environment(env) ------------------------------- ``env`` - jinja2 Environment The template environment that is being prepared This hook is called with the Jinja2 environment that is used to evaluate Datasette HTML templates. You can use it to do things like `register custom template filters `_, for example: .. code-block:: python from datasette import hookimpl @hookimpl def prepare_jinja2_environment(env): env.filters['uppercase'] = lambda u: u.upper() You can now use this filter in your custom templates like so:: Table name: {{ table|uppercase }} .. _plugin_hook_extra_template_vars: extra_template_vars(template, database, table, columns, view_name, request, datasette) -------------------------------------------------------------------------------------- Extra template variables that should be made available in the rendered template context. ``template`` - string The template that is being rendered, e.g. ``database.html`` ``database`` - string or None The name of the database, or ``None`` if the page does not correspond to a database (e.g. the root page) ``table`` - string or None The name of the table, or ``None`` if the page does not correct to a table ``columns`` - list of strings or None The names of the database columns that will be displayed on this page. ``None`` if the page does not contain a table. ``view_name`` - string The name of the view being displayed. (``index``, ``database``, ``table``, and ``row`` are the most important ones.) ``request`` - object or None The current HTTP :ref:`internals_request`. This can be ``None`` if the request object is not available. ``datasette`` - :ref:`internals_datasette` You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)`` This hook can return one of three different types: Dictionary If you return a dictionary its keys and values will be merged into the template context. 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. Datasette runs Jinja2 in `async mode `__, 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 adds a ``"user_agent"`` variable to the template context containing the current request's User-Agent header: .. code-block:: python @hookimpl def extra_template_vars(request): return { "user_agent": request.headers.get("user-agent") } This example returns an awaitable function which adds a list of ``hidden_table_names`` to the context: .. code-block:: python @hookimpl def extra_template_vars(datasette, database): async def hidden_table_names(): if database: db = datasette.databases[database] return {"hidden_table_names": await db.hidden_table_names()} else: return {} return hidden_table_names 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, 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] return {"sql_first": sql_first} You can then use the new function in a template like so:: SQLite version: {{ sql_first("select sqlite_version()") }} Examples: `datasette-search-all `_, `datasette-template-sql `_ .. _plugin_hook_extra_css_urls: extra_css_urls(template, database, table, columns, view_name, request, datasette) --------------------------------------------------------------------------------- Same arguments as :ref:`extra_template_vars(...) ` Return a list of extra CSS URLs that should be included on the page. These can take advantage of the CSS class hooks described in :ref:`customization`. This can be a list of URLs: .. code-block:: python from datasette import hookimpl @hookimpl def extra_css_urls(): return [ 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css' ] Or a list of dictionaries defining both a URL and an `SRI hash `_: .. code-block:: python from datasette import hookimpl @hookimpl def extra_css_urls(): return [{ 'url': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css', 'sri': 'sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4', }] This function can also return an awaitable function, useful if it needs to run any async code: .. code-block:: python from datasette import hookimpl @hookimpl def extra_css_urls(datasette): async def inner(): db = datasette.get_database() results = await db.execute("select url from css_files") return [r[0] for r in results] return inner Examples: `datasette-cluster-map `_, `datasette-vega `_ .. _plugin_hook_extra_js_urls: extra_js_urls(template, database, table, columns, view_name, request, datasette) -------------------------------------------------------------------------------- Same arguments as :ref:`extra_template_vars(...) ` This works in the same way as ``extra_css_urls()`` but for JavaScript. You can return a list of URLs, a list of dictionaries or an awaitable function that returns those things: .. code-block:: python from datasette import hookimpl @hookimpl def extra_js_urls(): return [{ 'url': 'https://code.jquery.com/jquery-3.3.1.slim.min.js', 'sri': 'sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo', }] You can also return URLs to files from your plugin's ``static/`` directory, if you have one: .. code-block:: python from datasette import hookimpl @hookimpl def extra_js_urls(): return [ '/-/static-plugins/your-plugin/app.js' ] Examples: `datasette-cluster-map `_, `datasette-vega `_ .. _plugin_hook_extra_body_script: extra_body_script(template, database, table, columns, view_name, request, datasette) ------------------------------------------------------------------------------------ Extra JavaScript to be added to a ``