mirror of
https://github.com/simonw/datasette.git
synced 2026-07-01 05:04:44 +02:00
Add cache-busted static asset helper (#2804)
* Add cache-busted static asset helper Add a static() helper for Datasette, plugin, and mounted static assets that appends content-based hashes, caches hashes in production, and serves matching hashed asset URLs with immutable far-future cache headers. Closes #2800
This commit is contained in:
parent
a4f74d1d2b
commit
5eca46a4bc
25 changed files with 361 additions and 65 deletions
|
|
@ -15,7 +15,8 @@ Unreleased
|
|||
- Create and alter table dialogs share their column-editing controls, including literal and expression defaults, custom column types, foreign keys and column ordering.
|
||||
- The "Write to this database" page now includes a Create table starter template, alongside the existing Insert, Update and Delete templates. (:pr:`2794`)
|
||||
- New :ref:`template_context` documentation listing the variables available to custom templates for Datasette's core pages. Variables documented there are treated as a stable API for custom templates until Datasette 2.0. The documentation is generated from dataclass definitions next to the view code, with tests that compare the documented fields against the actual contexts rendered by the database, table, query and row pages. (:issue:`1510`, :issue:`2127`, :issue:`1477`, :pr:`2803`)
|
||||
- Database and table pages now use the ``count_truncated`` template context value to display capped row counts as ``>N rows`.
|
||||
- New ``static()`` template function and ``datasette.static()`` method for generating cache-busting static asset URLs based on the file contents. Static assets served with a matching ``?_hash=`` parameter now receive far-future immutable cache headers. This works for Datasette's bundled static assets, plugin static assets and directories mounted using ``--static``. See :ref:`customization_static_files`.
|
||||
- Database and table pages now use the ``count_truncated`` template context value to display capped row counts as ``>N rows``.
|
||||
- Significant visual improvements to the table filter form UI, plus working add/remove filter buttons. (:issue:`2798`)
|
||||
- Improved edit row icon on table pages. (:issue:`2796`)
|
||||
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ If you want to change Datasette's Python code you can use the ``--reload`` optio
|
|||
|
||||
uv run datasette --reload fixtures.db
|
||||
|
||||
This also enables development mode for static asset cache busting, described in
|
||||
:ref:`customization_static_files`.
|
||||
|
||||
You can also use the ``fixtures.py`` script to recreate the testing version of ``metadata.json`` used by the unit tests. To do that::
|
||||
|
||||
uv run python tests/fixtures.py fixtures.db fixtures-metadata.json
|
||||
|
|
|
|||
|
|
@ -151,6 +151,45 @@ You can reference those files from ``datasette.yaml`` like this, see :ref:`custo
|
|||
}
|
||||
.. [[[end]]]
|
||||
|
||||
If you reference those files from a custom template, use the ``static()``
|
||||
template function to create a cache-busting URL based on the file contents.
|
||||
Pass the mount point as the ``mount=`` argument, and pass a path relative to
|
||||
that mounted directory:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<link rel="stylesheet" href="{{ static('styles.css', mount='assets') }}">
|
||||
<script src="{{ static('app.js', mount='assets') }}" defer></script>
|
||||
|
||||
The returned URL will include a ``?_hash=`` parameter and will take the
|
||||
``base_url`` setting into account. When that hash matches the current file
|
||||
contents, Datasette will serve the static asset with a far-future immutable
|
||||
``Cache-Control`` header. You can also use ``urls.path()`` if you want to link
|
||||
to the mounted file without adding a content hash:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<link rel="stylesheet" href="{{ urls.path('/assets/styles.css') }}">
|
||||
|
||||
When Datasette is run using ``--reload``, the file contents are hashed every time
|
||||
the template is rendered, so edits to static files will update their URLs
|
||||
without restarting Datasette. Without ``--reload``, the hash is cached for the
|
||||
lifetime of the Datasette process.
|
||||
|
||||
You can use the same function for Datasette's bundled static assets, omitting
|
||||
``mount=``:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<link rel="stylesheet" href="{{ static('app.css') }}">
|
||||
|
||||
For plugin static assets, pass the plugin name using ``plugin=`` and a path
|
||||
relative to the plugin's ``static/`` directory:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<script src="{{ static('plugin.js', plugin='datasette_plugin_name') }}" defer></script>
|
||||
|
||||
Publishing static assets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -451,6 +451,52 @@ await .render_template(template, context=None, request=None)
|
|||
|
||||
Renders a `Jinja template <https://jinja.palletsprojects.com/en/2.11.x/>`__ using Datasette's preconfigured instance of Jinja and returns the resulting string. The template will have access to Datasette's default template functions and any functions that have been made available by other plugins.
|
||||
|
||||
.. _datasette_static:
|
||||
|
||||
.static(path, plugin=None, mount=None)
|
||||
--------------------------------------
|
||||
|
||||
``path`` - string
|
||||
The path to the static asset, relative to the selected static directory.
|
||||
|
||||
``plugin`` - string, optional
|
||||
The plugin name, for linking to an asset in that plugin's ``static/``
|
||||
directory.
|
||||
|
||||
``mount`` - string, optional
|
||||
The ``--static`` mount name, for linking to an asset in a directory mounted
|
||||
using ``datasette --static mount_name:directory``.
|
||||
|
||||
Returns a URL for a static asset with a ``?_hash=`` parameter based on the file
|
||||
contents. That URL takes the ``base_url`` setting into account.
|
||||
|
||||
When the ``?_hash=`` parameter matches the current file contents, Datasette will
|
||||
serve the asset with ``Cache-Control: max-age=31536000, immutable, public``.
|
||||
|
||||
Call this with just ``path`` for one of Datasette's bundled static assets:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
datasette.static("app.css")
|
||||
|
||||
Use ``plugin=`` for plugin static assets:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
datasette.static(
|
||||
"plugin.js", plugin="datasette_plugin_name"
|
||||
)
|
||||
|
||||
Use ``mount=`` for static directories mounted using the ``--static`` option:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
datasette.static("styles.css", mount="assets")
|
||||
|
||||
``plugin`` and ``mount`` are mutually exclusive. The same feature is available
|
||||
to Jinja templates as the ``static()`` template function, described in
|
||||
:ref:`customization_static_files`.
|
||||
|
||||
.. _datasette_actors_from_ids:
|
||||
|
||||
await .actors_from_ids(actor_ids)
|
||||
|
|
|
|||
|
|
@ -1968,9 +1968,8 @@ Here is a minimal plugin example that adds a button to a table page and loads Ja
|
|||
@hookimpl
|
||||
def extra_js_urls(datasette):
|
||||
return [
|
||||
datasette.urls.static_plugins(
|
||||
"datasette_show_table",
|
||||
"show-table.js",
|
||||
datasette.static(
|
||||
"show-table.js", plugin="datasette_show_table"
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -47,15 +47,6 @@ These variables are available on every page rendered by Datasette, including pag
|
|||
``show_logout``
|
||||
True if the logout link should be shown in the navigation menu
|
||||
|
||||
``app_css_hash``
|
||||
Hash of Datasette's app.css contents, used for cache busting
|
||||
|
||||
``edit_tools_js_hash``
|
||||
Hash of Datasette's edit-tools.js contents, used for cache busting
|
||||
|
||||
``table_js_hash``
|
||||
Hash of Datasette's table.js contents, used for cache busting
|
||||
|
||||
``zip``
|
||||
Python's ``zip()`` builtin, made available to template logic
|
||||
|
||||
|
|
|
|||
|
|
@ -145,7 +145,16 @@ If your plugin has a ``static/`` directory, Datasette will automatically configu
|
|||
|
||||
/-/static-plugins/NAME_OF_PLUGIN_PACKAGE/yourfile.js
|
||||
|
||||
Use the ``datasette.urls.static_plugins(plugin_name, path)`` method to generate URLs to that asset that take the ``base_url`` setting into account, see :ref:`internals_datasette_urls`.
|
||||
Use the ``datasette.static(path, plugin=plugin_name)`` method to generate
|
||||
cache-busting URLs to those assets that take the ``base_url`` setting into
|
||||
account, see :ref:`datasette_static`.
|
||||
|
||||
This can also be used from plugin templates as the ``static()`` template
|
||||
function:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
<script src="{{ static('plugin.js', plugin='datasette_plugin_name') }}" defer></script>
|
||||
|
||||
To bundle the static assets for a plugin in the package that you publish to PyPI, add the following to the plugin's ``setup.py``:
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue