datasette.allowed_many() method

This commit is contained in:
Simon Willison 2026-06-12 12:51:40 -07:00
commit 88878b4184
6 changed files with 614 additions and 101 deletions

View file

@ -512,6 +512,39 @@ Example usage:
The method returns ``True`` if the permission is granted, ``False`` if denied.
.. _datasette_allowed_many:
await .allowed_many(\*, actions, resource, actor=None)
------------------------------------------------------
``actions`` - list of strings
The names of the actions to permission check.
``resource`` - Resource object
A Resource object representing the database, table, or other resource that each action is checked against. Omit for global actions.
``actor`` - dictionary, optional
The authenticated actor. This is usually ``request.actor``. Defaults to ``None`` for unauthenticated requests.
Checks several actions against the same resource for the same actor, returning a dictionary mapping each action name to ``True`` or ``False``. The whole batch - including any actions pulled in through ``also_requires`` dependencies - is resolved with a single SQL query against the internal database, so this is much faster than calling :ref:`datasette.allowed() <datasette_allowed>` once per action.
Example usage:
.. code-block:: python
from datasette.resources import TableResource
results = await datasette.allowed_many(
actions=["insert-row", "delete-row", "drop-table"],
resource=TableResource(
database="fixtures", table="facetable"
),
actor=request.actor,
)
# {"insert-row": True, "delete-row": True, "drop-table": False}
Actions for which no plugin provides any permission rules are resolved to ``False`` directly, without being included in the SQL query at all.
.. _datasette_allowed_resources:
await .allowed_resources(action, actor=None, \*, parent=None, include_is_private=False, include_reasons=False, limit=100, next=None)

View file

@ -1458,6 +1458,12 @@ to avoid conflicts with other plugins. The recommended convention is to prefix p
plugin's source name (e.g., ``myplugin_user_id``). The system reserves these parameter names:
``:actor``, ``:actor_id``, ``:action``, and ``:filter_parent``.
This hook may be called for many actions in rapid succession - for example
:ref:`datasette.allowed_many() <datasette_allowed_many>` gathers rules for every action in its batch
concurrently. Hook implementations must not assume that checks for different actions arrive one
page-render apart, and expensive work (such as network calls) should be cached independently of the
``action`` argument where possible.
You can also use return ``PermissionSQL.allow(reason="reason goes here")`` or ``PermissionSQL.deny(reason="reason goes here")`` as shortcuts for simple root-level allow or deny rules. These will create SQL snippets that look like this:
.. code-block:: sql