id,page,ref,title,content,breadcrumbs,references
authentication:actions,authentication,actions,Built-in actions,"This section lists all of the permission checks that are carried out by Datasette core, along with the resource if it was passed.","[""Authentication and permissions""]",[]
authentication:actions-alter-table,authentication,actions-alter-table,alter-table,"Actor is allowed to alter a database table.
resource - datasette.resources.TableResource(database, table)
database is the name of the database (string)
table is the name of the table (string)","[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-create-table,authentication,actions-create-table,create-table,"Actor is allowed to create a database table.
resource - datasette.resources.DatabaseResource(database)
database is the name of the database (string)","[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-debug-menu,authentication,actions-debug-menu,debug-menu,Controls if the various debug pages are displayed in the navigation menu.,"[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-delete-row,authentication,actions-delete-row,delete-row,"Actor is allowed to delete rows from a table.
resource - datasette.resources.TableResource(database, table)
database is the name of the database (string)
table is the name of the table (string)","[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-drop-table,authentication,actions-drop-table,drop-table,"Actor is allowed to drop a database table.
resource - datasette.resources.TableResource(database, table)
database is the name of the database (string)
table is the name of the table (string)","[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-execute-sql,authentication,actions-execute-sql,execute-sql,"Actor is allowed to run arbitrary SQL queries against a specific database, e.g. https://latest.datasette.io/fixtures/-/query?sql=select+100
resource - datasette.resources.DatabaseResource(database)
database is the name of the database (string)
See also the default_allow_sql setting .","[""Authentication and permissions"", ""Built-in actions""]","[{""href"": ""https://latest.datasette.io/fixtures/-/query?sql=select+100"", ""label"": ""https://latest.datasette.io/fixtures/-/query?sql=select+100""}]"
authentication:actions-insert-row,authentication,actions-insert-row,insert-row,"Actor is allowed to insert rows into a table.
resource - datasette.resources.TableResource(database, table)
database is the name of the database (string)
table is the name of the table (string)","[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-permissions-debug,authentication,actions-permissions-debug,permissions-debug,Actor is allowed to view the /-/permissions debug tools.,"[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-update-row,authentication,actions-update-row,update-row,"Actor is allowed to update rows in a table.
resource - datasette.resources.TableResource(database, table)
database is the name of the database (string)
table is the name of the table (string)","[""Authentication and permissions"", ""Built-in actions""]",[]
authentication:actions-view-database,authentication,actions-view-database,view-database,"Actor is allowed to view a database page, e.g. https://latest.datasette.io/fixtures
resource - datasette.permissions.DatabaseResource(database)
database is the name of the database (string)","[""Authentication and permissions"", ""Built-in actions""]","[{""href"": ""https://latest.datasette.io/fixtures"", ""label"": ""https://latest.datasette.io/fixtures""}]"
authentication:actions-view-database-download,authentication,actions-view-database-download,view-database-download,"Actor is allowed to download a database, e.g. https://latest.datasette.io/fixtures.db
resource - datasette.resources.DatabaseResource(database)
database is the name of the database (string)","[""Authentication and permissions"", ""Built-in actions""]","[{""href"": ""https://latest.datasette.io/fixtures.db"", ""label"": ""https://latest.datasette.io/fixtures.db""}]"
authentication:actions-view-instance,authentication,actions-view-instance,view-instance,"Top level permission - Actor is allowed to view any pages within this instance, starting at https://latest.datasette.io/","[""Authentication and permissions"", ""Built-in actions""]","[{""href"": ""https://latest.datasette.io/"", ""label"": ""https://latest.datasette.io/""}]"
authentication:actions-view-query,authentication,actions-view-query,view-query,"Actor is allowed to view (and execute) a canned query page, e.g. https://latest.datasette.io/fixtures/pragma_cache_size - this includes executing Writable canned queries .
resource - datasette.resources.QueryResource(database, query)
database is the name of the database (string)
query is the name of the canned query (string)","[""Authentication and permissions"", ""Built-in actions""]","[{""href"": ""https://latest.datasette.io/fixtures/pragma_cache_size"", ""label"": ""https://latest.datasette.io/fixtures/pragma_cache_size""}]"
authentication:actions-view-table,authentication,actions-view-table,view-table,"Actor is allowed to view a table (or view) page, e.g. https://latest.datasette.io/fixtures/complex_foreign_keys
resource - datasette.resources.TableResource(database, table)
database is the name of the database (string)
table is the name of the table (string)","[""Authentication and permissions"", ""Built-in actions""]","[{""href"": ""https://latest.datasette.io/fixtures/complex_foreign_keys"", ""label"": ""https://latest.datasette.io/fixtures/complex_foreign_keys""}]"
authentication:allowdebugview,authentication,allowdebugview,The /-/allow-debug tool,"The /-/allow-debug tool lets you try out different ""action"" blocks against different ""actor"" JSON objects. You can try that out here: https://latest.datasette.io/-/allow-debug","[""Authentication and permissions"", ""Permissions""]","[{""href"": ""https://latest.datasette.io/-/allow-debug"", ""label"": ""https://latest.datasette.io/-/allow-debug""}]"
authentication:allowedresourcesview,authentication,allowedresourcesview,Allowed resources view,"The /-/allowed endpoint displays resources that the current actor can access for a specified action .
This endpoint provides an interactive HTML form interface. Add .json to the URL path (e.g. /-/allowed.json ) to get the raw JSON response instead.
Pass ?action=view-table (or another action) to select the action. Optional parent= and child= query parameters can narrow the results to a specific database/table pair.
This endpoint is publicly accessible to help users understand their own permissions. The potentially sensitive reason field is only shown to users with the permissions-debug permission - it shows the plugins and explanatory reasons that were responsible for each decision.","[""Authentication and permissions"", ""Permissions debug tools""]",[]
authentication:authentication,authentication,authentication,Authentication and permissions,"Datasette doesn't require authentication by default. Any visitor to a Datasette instance can explore the full data and execute read-only SQL queries.
Datasette can be configured to only allow authenticated users, or to control which databases, tables, and queries can be accessed by the public or by specific users. Datasette's plugin system can be used to add many different styles of authentication, such as user accounts, single sign-on or API keys.",[],[]
authentication:authentication-actor,authentication,authentication-actor,Actors,"Through plugins, Datasette can support both authenticated users (with cookies) and authenticated API clients (via authentication tokens). The word ""actor"" is used to cover both of these cases.
Every request to Datasette has an associated actor value, available in the code as request.actor . This can be None for unauthenticated requests, or a JSON compatible Python dictionary for authenticated users or API clients.
The actor dictionary can be any shape - the design of that data structure is left up to the plugins. Actors should always include a unique ""id"" string, as demonstrated by the ""root"" actor below.
Plugins can use the actor_from_request(datasette, request) hook to implement custom logic for authenticating an actor based on the incoming HTTP request.","[""Authentication and permissions""]",[]
authentication:authentication-actor-matches-allow,authentication,authentication-actor-matches-allow,actor_matches_allow(),"Plugins that wish to implement this same ""allow"" block permissions scheme can take advantage of the datasette.utils.actor_matches_allow(actor, allow) function:
from datasette.utils import actor_matches_allow
actor_matches_allow({""id"": ""root""}, {""id"": ""*""})
# returns True
The currently authenticated actor is made available to plugins as request.actor .","[""Authentication and permissions""]",[]
authentication:authentication-cli-create-token,authentication,authentication-cli-create-token,datasette create-token,"You can also create tokens on the command line using the datasette create-token command.
This command takes one required argument - the ID of the actor to be associated with the created token.
You can specify a -e/--expires-after option in seconds. If omitted, the token will never expire.
The command will sign the token using the DATASETTE_SECRET environment variable, if available. You can also pass the secret using the --secret option.
This means you can run the command locally to create tokens for use with a deployed Datasette instance, provided you know that instance's secret.
To create a token for the root actor that will expire in one hour:
datasette create-token root --expires-after 3600
To create a token that never expires using a specific secret:
datasette create-token root --secret my-secret-goes-here","[""Authentication and permissions"", ""API Tokens""]",[]
authentication:authentication-cli-create-token-restrict,authentication,authentication-cli-create-token-restrict,Restricting the actions that a token can perform,"Tokens created using datasette create-token ACTOR_ID will inherit all of the permissions of the actor that they are associated with.
You can pass additional options to create tokens that are restricted to a subset of that actor's permissions.
To restrict the token to just specific permissions against all available databases, use the --all option:
datasette create-token root --all insert-row --all update-row
This option can be passed as many times as you like. In the above example the token will only be allowed to insert and update rows.
You can also restrict permissions such that they can only be used within specific databases:
datasette create-token root --database mydatabase insert-row
The resulting token will only be able to insert rows, and only to tables in the mydatabase database.
Finally, you can restrict permissions to individual resources - tables, SQL views and named queries - within a specific database:
datasette create-token root --resource mydatabase mytable insert-row
These options have short versions: -a for --all , -d for --database and -r for --resource .
You can add --debug to see a JSON representation of the token that has been created. Here's a full example:
datasette create-token root \
--secret mysecret \
--all view-instance \
--all view-table \
--database docs view-query \
--resource docs documents insert-row \
--resource docs documents update-row \
--debug
This example outputs the following:
dstok_.eJxFizEKgDAMRe_y5w4qYrFXERGxDkVsMI0uxbubdjFL8l_ez1jhwEQCA6Fjjxp90qtkuHawzdjYrh8MFobLxZ_wBH0_gtnAF-hpS5VfmF8D_lnd97lHqUJgLd6sls4H1qwlhA.nH_7RecYHj5qSzvjhMU95iy0Xlc
Decoded:
{
""a"": ""root"",
""token"": ""dstok"",
""t"": 1670907246,
""_r"": {
""a"": [
""vi"",
""vt""
],
""d"": {
""docs"": [
""vq""
]
},
""r"": {
""docs"": {
""documents"": [
""ir"",
""ur""
]
}
}
}
}
Restrictions act as an allowlist layered on top of the actor's existing
permissions. They can only remove access the actor would otherwise have—they
cannot grant new access. If the underlying actor is denied by allow rules in
datasette.yaml or by a plugin, a token that lists that resource in its
""_r"" section will still be denied.","[""Authentication and permissions"", ""API Tokens"", ""datasette create-token""]",[]
authentication:authentication-default-deny,authentication,authentication-default-deny,Denying all permissions by default,"By default, Datasette allows unauthenticated access to view databases, tables, and execute SQL queries.
You may want to run Datasette in a mode where all access is denied by default, and you explicitly grant permissions only to authenticated users, either using the --root mechanism or through configuration file rules or plugins.
Use the --default-deny command-line option to run Datasette in this mode:
datasette --default-deny data.db --root
With --default-deny enabled:
Anonymous users are denied access to view the instance, databases, tables, and queries
Authenticated users are also denied access unless they're explicitly granted permissions
The root user (when using --root ) still has access to everything
You can grant permissions using configuration file rules or plugins
For example, to allow only a specific user to access your instance:
datasette --default-deny data.db --config datasette.yaml
Where datasette.yaml contains:
allow:
id: alice
This configuration will deny access to everyone except the user with id of alice .","[""Authentication and permissions"", ""Permissions""]",[]
authentication:authentication-ds-actor,authentication,authentication-ds-actor,The ds_actor cookie,"Datasette includes a default authentication plugin which looks for a signed ds_actor cookie containing a JSON actor dictionary. This is how the root actor mechanism works.
Authentication plugins can set signed ds_actor cookies themselves like so:
response = Response.redirect(""/"")
datasette.set_actor_cookie(response, {""id"": ""cleopaws""})
The shape of data encoded in the cookie is as follows:
{
""a"": {
""id"": ""cleopaws""
}
}
To implement logout in a plugin, use the delete_actor_cookie() method:
response = Response.redirect(""/"")
datasette.delete_actor_cookie(response)","[""Authentication and permissions""]",[]
authentication:authentication-ds-actor-expiry,authentication,authentication-ds-actor-expiry,Including an expiry time,"ds_actor cookies can optionally include a signed expiry timestamp, after which the cookies will no longer be valid. Authentication plugins may chose to use this mechanism to limit the lifetime of the cookie. For example, if a plugin implements single-sign-on against another source it may decide to set short-lived cookies so that if the user is removed from the SSO system their existing Datasette cookies will stop working shortly afterwards.
To include an expiry pass expire_after= to datasette.set_actor_cookie() with a number of seconds. For example, to expire in 24 hours:
response = Response.redirect(""/"")
datasette.set_actor_cookie(
response, {""id"": ""cleopaws""}, expire_after=60 * 60 * 24
)
The resulting cookie will encode data that looks something like this:
{
""a"": {
""id"": ""cleopaws""
},
""e"": ""1jjSji""
}","[""Authentication and permissions"", ""The ds_actor cookie""]",[]
authentication:authentication-permissions,authentication,authentication-permissions,Permissions,"Datasette's permissions system is built around SQL queries. Datasette and its plugins construct SQL queries to resolve the list of resources that an actor cas access.
The key question the permissions system answers is this:
Is this actor allowed to perform this action , optionally against this particular resource ?
Actors are described above .
An action is a string describing the action the actor would like to perform. A full list is provided below - examples include view-table and execute-sql .
A resource is the item the actor wishes to interact with - for example a specific database or table. Some actions, such as permissions-debug , are not associated with a particular resource.
Datasette's built-in view actions ( view-database , view-table etc) are allowed by Datasette's default configuration: unless you configure additional permission rules unauthenticated users will be allowed to access content.
Other actions, including those introduced by plugins, will default to deny .","[""Authentication and permissions""]",[]
authentication:authentication-permissions-allow,authentication,authentication-permissions-allow,"Defining permissions with ""allow"" blocks","One way to define permissions in Datasette is to use an ""allow"" block in the datasette.yaml file . This is a JSON document describing which actors are allowed to perform an action against a specific resource.
Each allow block is compiled into SQL and combined with any
plugin-provided rules to produce
the cascading allow/deny decisions that power await .allowed(*, action, resource, actor=None) .
The most basic form of allow block is this ( allow demo , deny demo ):
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow:
id: root
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
This will match any actors with an ""id"" property of ""root"" - for example, an actor that looks like this:
{
""id"": ""root"",
""name"": ""Root User""
}
An allow block can specify ""deny all"" using false ( demo ):
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow: false
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
An ""allow"" of true allows all access ( demo ):
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow: true
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
Allow keys can provide a list of values. These will match any actor that has any of those values ( allow demo , deny demo ):
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow:
id:
- simon
- cleopaws
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
This will match any actor with an ""id"" of either ""simon"" or ""cleopaws"" .
Actors can have properties that feature a list of values. These will be matched against the list of values in an allow block. Consider the following actor:
{
""id"": ""simon"",
""roles"": [""staff"", ""developer""]
}
This allow block will provide access to any actor that has ""developer"" as one of their roles ( allow demo , deny demo ):
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow:
roles:
- developer
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
Note that ""roles"" is not a concept that is baked into Datasette - it's a convention that plugins can choose to implement and act on.
If you want to provide access to any actor with a value for a specific key, use ""*"" . For example, to match any logged-in user specify the following ( allow demo , deny demo ):
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow:
id: ""*""
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
You can specify that only unauthenticated actors (from anonymous HTTP requests) should be allowed access using the special ""unauthenticated"": true key in an allow block ( allow demo , deny demo ):
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow:
unauthenticated: true
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
Allow keys act as an ""or"" mechanism. An actor will be able to execute the query if any of their JSON properties match any of the values in the corresponding lists in the allow block. The following block will allow users with either a role of ""ops"" OR users who have an id of ""simon"" or ""cleopaws"" :
[[[cog
from metadata_doc import config_example
import textwrap
config_example(cog, textwrap.dedent(
""""""
allow:
id:
- simon
- cleopaws
role: ops
"""""").strip(),
""YAML"", ""JSON""
)
]]]
[[[end]]]
Demo for cleopaws , demo for ops role , demo for an actor matching neither rule .","[""Authentication and permissions"", ""Permissions""]","[{""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%22id%22%3A+%22root%22%7D&allow=%7B%0D%0A++++++++%22id%22%3A+%22root%22%0D%0A++++%7D"", ""label"": ""allow demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%22id%22%3A+%22trevor%22%7D&allow=%7B%0D%0A++++++++%22id%22%3A+%22root%22%0D%0A++++%7D"", ""label"": ""deny demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22root%22%0D%0A%7D&allow=false"", ""label"": ""demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22root%22%0D%0A%7D&allow=true"", ""label"": ""demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22cleopaws%22%0D%0A%7D&allow=%7B%0D%0A++++%22id%22%3A+%5B%0D%0A++++++++%22simon%22%2C%0D%0A++++++++%22cleopaws%22%0D%0A++++%5D%0D%0A%7D"", ""label"": ""allow demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22pancakes%22%0D%0A%7D&allow=%7B%0D%0A++++%22id%22%3A+%5B%0D%0A++++++++%22simon%22%2C%0D%0A++++++++%22cleopaws%22%0D%0A++++%5D%0D%0A%7D"", ""label"": ""deny demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22simon%22%2C%0D%0A++++%22roles%22%3A+%5B%0D%0A++++++++%22staff%22%2C%0D%0A++++++++%22developer%22%0D%0A++++%5D%0D%0A%7D&allow=%7B%0D%0A++++%22roles%22%3A+%5B%0D%0A++++++++%22developer%22%0D%0A++++%5D%0D%0A%7D"", ""label"": ""allow demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22cleopaws%22%2C%0D%0A++++%22roles%22%3A+%5B%22dog%22%5D%0D%0A%7D&allow=%7B%0D%0A++++%22roles%22%3A+%5B%0D%0A++++++++%22developer%22%0D%0A++++%5D%0D%0A%7D"", ""label"": ""deny demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22simon%22%0D%0A%7D&allow=%7B%0D%0A++++%22id%22%3A+%22*%22%0D%0A%7D"", ""label"": ""allow demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22bot%22%3A+%22readme-bot%22%0D%0A%7D&allow=%7B%0D%0A++++%22id%22%3A+%22*%22%0D%0A%7D"", ""label"": ""deny demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=null&allow=%7B%0D%0A++++%22unauthenticated%22%3A+true%0D%0A%7D"", ""label"": ""allow demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22hello%22%0D%0A%7D&allow=%7B%0D%0A++++%22unauthenticated%22%3A+true%0D%0A%7D"", ""label"": ""deny demo""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22cleopaws%22%0D%0A%7D&allow=%7B%0D%0A++++%22id%22%3A+%5B%0D%0A++++++++%22simon%22%2C%0D%0A++++++++%22cleopaws%22%0D%0A++++%5D%2C%0D%0A++++%22role%22%3A+%22ops%22%0D%0A%7D"", ""label"": ""Demo for cleopaws""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22trevor%22%2C%0D%0A++++%22role%22%3A+%5B%0D%0A++++++++%22ops%22%2C%0D%0A++++++++%22staff%22%0D%0A++++%5D%0D%0A%7D&allow=%7B%0D%0A++++%22id%22%3A+%5B%0D%0A++++++++%22simon%22%2C%0D%0A++++++++%22cleopaws%22%0D%0A++++%5D%2C%0D%0A++++%22role%22%3A+%22ops%22%0D%0A%7D"", ""label"": ""demo for ops role""}, {""href"": ""https://latest.datasette.io/-/allow-debug?actor=%7B%0D%0A++++%22id%22%3A+%22percy%22%2C%0D%0A++++%22role%22%3A+%5B%0D%0A++++++++%22staff%22%0D%0A++++%5D%0D%0A%7D&allow=%7B%0D%0A++++%22id%22%3A+%5B%0D%0A++++++++%22simon%22%2C%0D%0A++++++++%22cleopaws%22%0D%0A++++%5D%2C%0D%0A++++%22role%22%3A+%22ops%22%0D%0A%7D"", ""label"": ""demo for an actor matching neither rule""}]"
authentication:authentication-permissions-config,authentication,authentication-permissions-config,Access permissions in ,"There are two ways to configure permissions using datasette.yaml (or datasette.json ).
For simple visibility permissions you can use ""allow"" blocks in the root, database, table and query sections.
For other permissions you can use a ""permissions"" block, described in the next section .
You can limit who is allowed to view different parts of your Datasette instance using ""allow"" keys in your Configuration .
You can control the following:
Access to the entire Datasette instance
Access to specific databases
Access to specific tables and views
Access to specific Canned queries
If a user has permission to view a table they will be able to view that table, independent of if they have permission to view the database or instance that the table exists within.","[""Authentication and permissions""]",[]
authentication:authentication-permissions-database,authentication,authentication-permissions-database,Access to specific databases,"To limit access to a specific private.db database to just authenticated users, use the ""allow"" block like this:
[[[cog
config_example(cog, """"""
databases:
private:
allow:
id: ""*""
"""""")
]]]
[[[end]]]","[""Authentication and permissions"", ""Access permissions in ""]",[]
authentication:authentication-permissions-execute-sql,authentication,authentication-permissions-execute-sql,Controlling the ability to execute arbitrary SQL,"Datasette defaults to allowing any site visitor to execute their own custom SQL queries, for example using the form on the database page or by appending a ?_where= parameter to the table page like this .
Access to this ability is controlled by the execute-sql permission.
The easiest way to disable arbitrary SQL queries is using the default_allow_sql setting when you first start Datasette running.
You can alternatively use an ""allow_sql"" block to control who is allowed to execute arbitrary SQL queries.
To prevent any user from executing arbitrary SQL queries, use this:
[[[cog
config_example(cog, """"""
allow_sql: false
"""""")
]]]
[[[end]]]
To enable just the root user to execute SQL for all databases in your instance, use the following:
[[[cog
config_example(cog, """"""
allow_sql:
id: root
"""""")
]]]
[[[end]]]
To limit this ability for just one specific database, use this:
[[[cog
config_example(cog, """"""
databases:
mydatabase:
allow_sql:
id: root
"""""")
]]]
[[[end]]]","[""Authentication and permissions"", ""Access permissions in ""]","[{""href"": ""https://latest.datasette.io/fixtures"", ""label"": ""the database page""}, {""href"": ""https://latest.datasette.io/fixtures/facetable?_where=_city_id=1"", ""label"": ""like this""}]"
authentication:authentication-permissions-explained,authentication,authentication-permissions-explained,How permissions are resolved,"Datasette performs permission checks using the internal await .allowed(*, action, resource, actor=None) , method which accepts keyword arguments for action , resource and an optional actor .
resource should be an instance of the appropriate Resource subclass from datasette.resources —for example InstanceResource() , DatabaseResource(database=""... )`` or TableResource(database=""..."", table=""..."") . This defaults to InstanceResource() if not specified.
When a check runs Datasette gathers allow/deny rules from multiple sources and
compiles them into a SQL query. The resulting query describes all of the
resources an actor may access for that action, together with the reasons those
resources were allowed or denied. The combined sources are:
allow blocks configured in datasette.yaml .
Actor restrictions encoded into the actor dictionary or API token.
The ""root"" user shortcut when --root (or Datasette.root_enabled ) is active, replying True to all permission chucks unless configuration rules deny them at a more specific level.
Any additional SQL provided by plugins implementing permission_resources_sql(datasette, actor, action) .
Datasette evaluates the SQL to determine if the requested resource is
included. Explicit deny rules returned by configuration or plugins will block
access even if other rules allowed it.","[""Authentication and permissions"", ""Permissions""]",[]
authentication:authentication-permissions-instance,authentication,authentication-permissions-instance,Access to an instance,"Here's how to restrict access to your entire Datasette instance to just the ""id"": ""root"" user:
[[[cog
from metadata_doc import config_example
config_example(cog, """"""
title: My private Datasette instance
allow:
id: root
"""""")
]]]
[[[end]]]
To deny access to all users, you can use ""allow"": false :
[[[cog
config_example(cog, """"""
title: My entirely inaccessible instance
allow: false
"""""")
]]]
[[[end]]]
One reason to do this is if you are using a Datasette plugin - such as datasette-permissions-sql - to control permissions instead.","[""Authentication and permissions"", ""Access permissions in ""]","[{""href"": ""https://github.com/simonw/datasette-permissions-sql"", ""label"": ""datasette-permissions-sql""}]"
authentication:authentication-permissions-other,authentication,authentication-permissions-other,Other permissions in ,"For all other permissions, you can use one or more ""permissions"" blocks in your datasette.yaml configuration file.
To grant access to the permissions debug tool to all signed in users, you can grant permissions-debug to any actor with an id matching the wildcard * by adding this a the root of your configuration:
[[[cog
config_example(cog, """"""
permissions:
debug-menu:
id: '*'
"""""")
]]]
[[[end]]]
To grant create-table to the user with id of editor for the docs database:
[[[cog
config_example(cog, """"""
databases:
docs:
permissions:
create-table:
id: editor
"""""")
]]]
[[[end]]]
And for insert-row against the reports table in that docs database:
[[[cog
config_example(cog, """"""
databases:
docs:
tables:
reports:
permissions:
insert-row:
id: editor
"""""")
]]]
[[[end]]]
The permissions debug tool can be useful for helping test permissions that you have configured in this way.","[""Authentication and permissions""]",[]
authentication:authentication-permissions-query,authentication,authentication-permissions-query,Access to specific canned queries,"Canned queries allow you to configure named SQL queries in your datasette.yaml that can be executed by users. These queries can be set up to both read and write to the database, so controlling who can execute them can be important.
To limit access to the add_name canned query in your dogs.db database to just the root user :
[[[cog
config_example(cog, """"""
databases:
dogs:
queries:
add_name:
sql: INSERT INTO names (name) VALUES (:name)
write: true
allow:
id:
- root
"""""")
]]]
[[[end]]]","[""Authentication and permissions"", ""Access permissions in ""]",[]
authentication:authentication-permissions-table,authentication,authentication-permissions-table,Access to specific tables and views,"To limit access to the users table in your bakery.db database:
[[[cog
config_example(cog, """"""
databases:
bakery:
tables:
users:
allow:
id: '*'
"""""")
]]]
[[[end]]]
This works for SQL views as well - you can list their names in the ""tables"" block above in the same way as regular tables.
Restricting access to tables and views in this way will NOT prevent users from querying them using arbitrary SQL queries, like this for example.
If you are restricting access to specific tables you should also use the ""allow_sql"" block to prevent users from bypassing the limit with their own SQL queries - see Controlling the ability to execute arbitrary SQL .","[""Authentication and permissions"", ""Access permissions in ""]","[{""href"": ""https://latest.datasette.io/fixtures?sql=select+*+from+facetable"", ""label"": ""like this""}]"
authentication:authentication-root,authentication,authentication-root,"Using the ""root"" actor","Datasette currently leaves almost all forms of authentication to plugins - datasette-auth-github for example.
The one exception is the ""root"" account, which you can sign into while using Datasette on your local machine. The root user has all permissions - they can perform any action regardless of other permission rules.
The --root flag is designed for local development and testing. When you start Datasette with --root , the root user automatically receives every permission, including:
All view permissions ( view-instance , view-database , view-table , etc.)
All write permissions ( insert-row , update-row , delete-row , create-table , alter-table , drop-table )
Debug permissions ( permissions-debug , debug-menu )
Any custom permissions defined by plugins
If you add explicit deny rules in datasette.yaml those can still block the
root actor from specific databases or tables.
The --root flag sets an internal root_enabled switch—without it, a signed-in user with {""id"": ""root""} is treated like any other actor.
To sign in as root, start Datasette using the --root command-line option, like this:
datasette --root
Datasette will output a single-use-only login URL on startup:
http://127.0.0.1:8001/-/auth-token?token=786fc524e0199d70dc9a581d851f466244e114ca92f33aa3b42a139e9388daa7
INFO: Started server process [25801]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8001 (Press CTRL+C to quit)
Click on that link and then visit http://127.0.0.1:8001/-/actor to confirm that you are authenticated as an actor that looks like this:
{
""id"": ""root""
}","[""Authentication and permissions"", ""Actors""]","[{""href"": ""https://github.com/simonw/datasette-auth-github"", ""label"": ""datasette-auth-github""}]"
authentication:createtokenview,authentication,createtokenview,API Tokens,"Datasette includes a default mechanism for generating API tokens that can be used to authenticate requests.
Authenticated users can create new API tokens using a form on the /-/create-token page.
Tokens created in this way can be further restricted to only allow access to specific actions, or to limit those actions to specific databases, tables or queries.
Created tokens can then be passed in the Authorization: Bearer $token header of HTTP requests to Datasette.
A token created by a user will include that user's ""id"" in the token payload, so any permissions granted to that user based on their ID can be made available to the token as well.
When one of these a token accompanies a request, the actor for that request will have the following shape:
{
""id"": ""user_id"",
""token"": ""dstok"",
""token_expires"": 1667717426
}
The ""id"" field duplicates the ID of the actor who first created the token.
The ""token"" field identifies that this actor was authenticated using a Datasette signed token ( dstok ).
The ""token_expires"" field, if present, indicates that the token will expire after that integer timestamp.
The /-/create-token page cannot be accessed by actors that are authenticated with a ""token"": ""some-value"" property. This is to prevent API tokens from being used to create more tokens.
Datasette plugins that implement their own form of API token authentication should follow this convention.
You can disable the signed token feature entirely using the allow_signed_tokens setting.","[""Authentication and permissions""]",[]
authentication:logoutview,authentication,logoutview,The /-/logout page,The page at /-/logout provides the ability to log out of a ds_actor cookie authentication session.,"[""Authentication and permissions"", ""The ds_actor cookie""]",[]
authentication:permissioncheckview,authentication,permissioncheckview,Permission check view,"The /-/check endpoint evaluates a single action/resource pair and returns information indicating whether the access was allowed along with diagnostic information.
This endpoint provides an interactive HTML form interface. Add .json to the URL path (e.g. /-/check.json?action=view-instance ) to get the raw JSON response instead.
Pass ?action= to specify the action to check, and optional ?parent= and ?child= parameters to specify the resource.","[""Authentication and permissions"", ""Permissions debug tools""]",[]
authentication:permissionrulesview,authentication,permissionrulesview,Permission rules view,"The /-/rules endpoint displays all permission rules (both allow and deny) for each candidate resource for the requested action.
This endpoint provides an interactive HTML form interface. Add .json to the URL path (e.g. /-/rules.json?action=view-table ) to get the raw JSON response instead.
Pass ?action= as a query parameter to specify which action to check.
This endpoint requires the permissions-debug permission.","[""Authentication and permissions"", ""Permissions debug tools""]",[]
authentication:permissions-plugins,authentication,permissions-plugins,Checking permissions in plugins,"Datasette plugins can check if an actor has permission to perform an action using await .allowed(*, action, resource, actor=None) —for example:
from datasette.resources import TableResource
can_edit = await datasette.allowed(
action=""update-row"",
resource=TableResource(database=""fixtures"", table=""facetable""),
actor=request.actor,
)
Use await .ensure_permission(action, resource=None, actor=None) when you need to enforce a permission and
raise a Forbidden error automatically.
Plugins that define new operations should return Action
objects from register_actions(datasette) and can supply additional allow/deny
rules by returning PermissionSQL objects from the
permission_resources_sql(datasette, actor, action) hook. Those rules are merged with
configuration allow blocks and actor restrictions to determine the final
result for each check.","[""Authentication and permissions""]",[]
authentication:permissionsdebugview,authentication,permissionsdebugview,Permissions debug tools,"The debug tool at /-/permissions is available to any actor with the permissions-debug permission. By default this is just the authenticated root user but you can open it up to all users by starting Datasette like this:
datasette -s permissions.permissions-debug true data.db
The page shows the permission checks that have been carried out by the Datasette instance.
It also provides an interface for running hypothetical permission checks against a hypothetical actor. This is a useful way of confirming that your configured permissions work in the way you expect.
This is designed to help administrators and plugin authors understand exactly how permission checks are being carried out, in order to effectively configure Datasette's permission system.","[""Authentication and permissions""]",[]
binary_data:binary,binary_data,binary,Binary data,"SQLite tables can contain binary data in BLOB columns.
Datasette includes special handling for these binary values. The Datasette interface detects binary values and provides a link to download their content, for example on https://latest.datasette.io/fixtures/binary_data
Binary data is represented in .json exports using Base64 encoding.
https://latest.datasette.io/fixtures/binary_data.json?_shape=array
[
{
""rowid"": 1,
""data"": {
""$base64"": true,
""encoded"": ""FRwCx60F/g==""
}
},
{
""rowid"": 2,
""data"": {
""$base64"": true,
""encoded"": ""FRwDx60F/g==""
}
},
{
""rowid"": 3,
""data"": null
}
]",[],"[{""href"": ""https://latest.datasette.io/fixtures/binary_data"", ""label"": ""https://latest.datasette.io/fixtures/binary_data""}, {""href"": ""https://latest.datasette.io/fixtures/binary_data.json?_shape=array"", ""label"": ""https://latest.datasette.io/fixtures/binary_data.json?_shape=array""}]"
binary_data:binary-linking,binary_data,binary-linking,Linking to binary downloads,"The .blob output format is used to return binary data. It requires a _blob_column= query string argument specifying which BLOB column should be downloaded, for example:
https://latest.datasette.io/fixtures/binary_data/1.blob?_blob_column=data
This output format can also be used to return binary data from an arbitrary SQL query. Since such queries do not specify an exact row, an additional ?_blob_hash= parameter can be used to specify the SHA-256 hash of the value that is being linked to.
Consider the query select data from binary_data - demonstrated here .
That page links to the binary value downloads. Those links look like this:
https://latest.datasette.io/fixtures.blob?sql=select+data+from+binary_data&_blob_column=data&_blob_hash=f3088978da8f9aea479ffc7f631370b968d2e855eeb172bea7f6c7a04262bb6d
These .blob links are also returned in the .csv exports Datasette provides for binary tables and queries, since the CSV format does not have a mechanism for representing binary data.","[""Binary data""]","[{""href"": ""https://latest.datasette.io/fixtures/binary_data/1.blob?_blob_column=data"", ""label"": ""https://latest.datasette.io/fixtures/binary_data/1.blob?_blob_column=data""}, {""href"": ""https://latest.datasette.io/fixtures?sql=select+data+from+binary_data"", ""label"": ""demonstrated here""}, {""href"": ""https://latest.datasette.io/fixtures.blob?sql=select+data+from+binary_data&_blob_column=data&_blob_hash=f3088978da8f9aea479ffc7f631370b968d2e855eeb172bea7f6c7a04262bb6d"", ""label"": ""https://latest.datasette.io/fixtures.blob?sql=select+data+from+binary_data&_blob_column=data&_blob_hash=f3088978da8f9aea479ffc7f631370b968d2e855eeb172bea7f6c7a04262bb6d""}]"
binary_data:binary-plugins,binary_data,binary-plugins,Binary plugins,"Several Datasette plugins are available that change the way Datasette treats binary data.
datasette-render-binary modifies Datasette's default interface to show an automatic guess at what type of binary data is being stored, along with a visual representation of the binary value that displays ASCII strings directly in the interface.
datasette-render-images detects common image formats and renders them as images directly in the Datasette interface.
datasette-media allows Datasette interfaces to be configured to serve binary files from configured SQL queries, and includes the ability to resize images directly before serving them.","[""Binary data""]","[{""href"": ""https://github.com/simonw/datasette-render-binary"", ""label"": ""datasette-render-binary""}, {""href"": ""https://github.com/simonw/datasette-render-images"", ""label"": ""datasette-render-images""}, {""href"": ""https://github.com/simonw/datasette-media"", ""label"": ""datasette-media""}]"
changelog:alter-table-support-for-create-insert-upsert-and-update,changelog,alter-table-support-for-create-insert-upsert-and-update,"Alter table support for create, insert, upsert and update","The JSON write API can now be used to apply simple alter table schema changes, provided the acting actor has the new alter-table permission. ( #2101 )
The only alter operation supported so far is adding new columns to an existing table.
The /db/-/create API now adds new columns during large operations to create a table based on incoming example ""rows"" , in the case where one of the later rows includes columns that were not present in the earlier batches. This requires the create-table but not the alter-table permission.
When /db/-/create is called with rows in a situation where the table may have been already created, an ""alter"": true key can be included to indicate that any missing columns from the new rows should be added to the table. This requires the alter-table permission.
/db/table/-/insert and /db/table/-/upsert and /db/table/row-pks/-/update all now also accept ""alter"": true , depending on the alter-table permission.
Operations that alter a table now fire the new alter-table event .","[""Changelog"", ""1.0a9 (2024-02-16)""]","[{""href"": ""https://github.com/simonw/datasette/issues/2101"", ""label"": ""#2101""}]"
changelog:asgi,changelog,asgi,ASGI,"ASGI is the Asynchronous Server Gateway Interface standard. I've been wanting to convert Datasette into an ASGI application for over a year - Port Datasette to ASGI #272 tracks thirteen months of intermittent development - but with Datasette 0.29 the change is finally released. This also means Datasette now runs on top of Uvicorn and no longer depends on Sanic .
I wrote about the significance of this change in Porting Datasette to ASGI, and Turtles all the way down .
The most exciting consequence of this change is that Datasette plugins can now take advantage of the ASGI standard.","[""Changelog"", ""0.29 (2019-07-07)""]","[{""href"": ""https://asgi.readthedocs.io/"", ""label"": ""ASGI""}, {""href"": ""https://github.com/simonw/datasette/issues/272"", ""label"": ""Port Datasette to ASGI #272""}, {""href"": ""https://www.uvicorn.org/"", ""label"": ""Uvicorn""}, {""href"": ""https://github.com/huge-success/sanic"", ""label"": ""Sanic""}, {""href"": ""https://simonwillison.net/2019/Jun/23/datasette-asgi/"", ""label"": ""Porting Datasette to ASGI, and Turtles all the way down""}]"
changelog:authentication,changelog,authentication,Authentication,"Prior to this release the Datasette ecosystem has treated authentication as exclusively the realm of plugins, most notably through datasette-auth-github .
0.44 introduces Authentication and permissions as core Datasette concepts ( #699 ). This enables different plugins to share responsibility for authenticating requests - you might have one plugin that handles user accounts and another one that allows automated access via API keys, for example.
You'll need to install plugins if you want full user accounts, but default Datasette can now authenticate a single root user with the new --root command-line option, which outputs a one-time use URL to authenticate as a root actor ( #784 ):
datasette fixtures.db --root
http://127.0.0.1:8001/-/auth-token?token=5b632f8cd44b868df625f5a6e2185d88eea5b22237fd3cc8773f107cc4fd6477
INFO: Started server process [14973]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8001 (Press CTRL+C to quit)
Plugins can implement new ways of authenticating users using the new actor_from_request(datasette, request) hook.","[""Changelog"", ""0.44 (2020-06-11)""]","[{""href"": ""https://github.com/simonw/datasette-auth-github"", ""label"": ""datasette-auth-github""}, {""href"": ""https://github.com/simonw/datasette/issues/699"", ""label"": ""#699""}, {""href"": ""https://github.com/simonw/datasette/issues/784"", ""label"": ""#784""}]"
changelog:better-plugin-documentation,changelog,better-plugin-documentation,Better plugin documentation,"The plugin documentation has been re-arranged into four sections, including a brand new section on testing plugins. ( #687 )
Plugins introduces Datasette's plugin system and describes how to install and configure plugins.
Writing plugins describes how to author plugins, from one-off single file plugins to packaged plugins that can be published to PyPI. It also describes how to start a plugin using the new datasette-plugin cookiecutter template.
Plugin hooks is a full list of detailed documentation for every Datasette plugin hook.
Testing plugins describes how to write tests for Datasette plugins, using pytest and HTTPX .","[""Changelog"", ""0.45 (2020-07-01)""]","[{""href"": ""https://github.com/simonw/datasette/issues/687"", ""label"": ""#687""}, {""href"": ""https://github.com/simonw/datasette-plugin"", ""label"": ""datasette-plugin""}, {""href"": ""https://docs.pytest.org/"", ""label"": ""pytest""}, {""href"": ""https://www.python-httpx.org/"", ""label"": ""HTTPX""}]"
changelog:binary-data,changelog,binary-data,Binary data,"SQLite tables can contain binary data in BLOB columns. Datasette now provides links for users to download this data directly from Datasette, and uses those links to make binary data available from CSV exports. See Binary data for more details. ( #1036 and #1034 ).","[""Changelog"", ""0.51 (2020-10-31)""]","[{""href"": ""https://github.com/simonw/datasette/issues/1036"", ""label"": ""#1036""}, {""href"": ""https://github.com/simonw/datasette/issues/1034"", ""label"": ""#1034""}]"
changelog:bug-fixes,changelog,bug-fixes,Bug fixes,"Don't show the facet option in the cog menu if faceting is not allowed. ( #1683 )
?_sort and ?_sort_desc now work if the column that is being sorted has been excluded from the query using ?_col= or ?_nocol= . ( #1773 )
Fixed bug where ?_sort_desc was duplicated in the URL every time the Apply button was clicked. ( #1738 )","[""Changelog"", ""0.62 (2022-08-14)""]","[{""href"": ""https://github.com/simonw/datasette/issues/1683"", ""label"": ""#1683""}, {""href"": ""https://github.com/simonw/datasette/issues/1773"", ""label"": ""#1773""}, {""href"": ""https://github.com/simonw/datasette/issues/1738"", ""label"": ""#1738""}]"
changelog:bug-fixes-and-other-improvements,changelog,bug-fixes-and-other-improvements,Bug fixes and other improvements,"Custom pages now work correctly when combined with the base_url setting. ( #1238 )
Fixed intermittent error displaying the index page when the user did not have permission to access one of the tables. Thanks, Guy Freeman. ( #1305 )
Columns with the name ""Link"" are no longer incorrectly displayed in bold. ( #1308 )
Fixed error caused by tables with a single quote in their names. ( #1257 )
Updated dependencies: pytest-asyncio , Black , jinja2 , aiofiles , click , and itsdangerous .
The official Datasette Docker image now supports apt-get install . ( #1320 )
The Heroku runtime used by datasette publish heroku is now python-3.8.10 .","[""Changelog"", ""0.57 (2021-06-05)""]","[{""href"": ""https://github.com/simonw/datasette/issues/1238"", ""label"": ""#1238""}, {""href"": ""https://github.com/simonw/datasette/issues/1305"", ""label"": ""#1305""}, {""href"": ""https://github.com/simonw/datasette/issues/1308"", ""label"": ""#1308""}, {""href"": ""https://github.com/simonw/datasette/issues/1257"", ""label"": ""#1257""}, {""href"": ""https://github.com/simonw/datasette/issues/1320"", ""label"": ""#1320""}]"
changelog:code-formatting-with-black-and-prettier,changelog,code-formatting-with-black-and-prettier,Code formatting with Black and Prettier,"Datasette adopted Black for opinionated Python code formatting in June 2019. Datasette now also embraces Prettier for JavaScript formatting, which like Black is enforced by tests in continuous integration. Instructions for using these two tools can be found in the new section on Code formatting in the contributors documentation. ( #1167 )","[""Changelog"", ""0.54 (2021-01-25)""]","[{""href"": ""https://github.com/psf/black"", ""label"": ""Black""}, {""href"": ""https://prettier.io/"", ""label"": ""Prettier""}, {""href"": ""https://github.com/simonw/datasette/issues/1167"", ""label"": ""#1167""}]"
changelog:configuration,changelog,configuration,Configuration,"Plugin configuration now lives in the datasette.yaml configuration file , passed to Datasette using the -c/--config option. Thanks, Alex Garcia. ( #2093 )
datasette -c datasette.yaml
Where datasette.yaml contains configuration that looks like this:
plugins:
datasette-cluster-map:
latitude_column: xlat
longitude_column: xlon
Previously plugins were configured in metadata.yaml , which was confusing as plugin settings were unrelated to database and table metadata.
The -s/--setting option can now be used to set plugin configuration as well. See Configuration via the command-line for details. ( #2252 )
The above YAML configuration example using -s/--setting looks like this:
datasette mydatabase.db \
-s plugins.datasette-cluster-map.latitude_column xlat \
-s plugins.datasette-cluster-map.longitude_column xlon
The new /-/config page shows the current instance configuration, after redacting keys that could contain sensitive data such as API keys or passwords. ( #2254 )
Existing Datasette installations may already have configuration set in metadata.yaml that should be migrated to datasette.yaml . To avoid breaking these installations, Datasette will silently treat table configuration, plugin configuration and allow blocks in metadata as if they had been specified in configuration instead. ( #2247 ) ( #2248 ) ( #2249 )
Note that the datasette publish command has not yet been updated to accept a datasette.yaml configuration file. This will be addressed in #2195 but for the moment you can include those settings in metadata.yaml instead.","[""Changelog"", ""1.0a8 (2024-02-07)""]","[{""href"": ""https://github.com/simonw/datasette/issues/2093"", ""label"": ""#2093""}, {""href"": ""https://github.com/simonw/datasette/issues/2252"", ""label"": ""#2252""}, {""href"": ""https://github.com/simonw/datasette/issues/2254"", ""label"": ""#2254""}, {""href"": ""https://github.com/simonw/datasette/issues/2247"", ""label"": ""#2247""}, {""href"": ""https://github.com/simonw/datasette/issues/2248"", ""label"": ""#2248""}, {""href"": ""https://github.com/simonw/datasette/issues/2249"", ""label"": ""#2249""}, {""href"": ""https://github.com/simonw/datasette/issues/2195"", ""label"": ""#2195""}]"
changelog:control-http-caching-with-ttl,changelog,control-http-caching-with-ttl,Control HTTP caching with ?_ttl=,"You can now customize the HTTP max-age header that is sent on a per-URL basis, using the new ?_ttl= query string parameter.
You can set this to any value in seconds, or you can set it to 0 to disable HTTP caching entirely.
Consider for example this query which returns a randomly selected member of the Avengers:
select * from [avengers/avengers] order by random() limit 1
If you hit the following page repeatedly you will get the same result, due to HTTP caching:
/fivethirtyeight?sql=select+*+from+%5Bavengers%2Favengers%5D+order+by+random%28%29+limit+1
By adding ?_ttl=0 to the zero you can ensure the page will not be cached and get back a different super hero every time:
/fivethirtyeight?sql=select+*+from+%5Bavengers%2Favengers%5D+order+by+random%28%29+limit+1&_ttl=0","[""Changelog"", ""0.23 (2018-06-18)""]","[{""href"": ""https://fivethirtyeight.datasettes.com/fivethirtyeight?sql=select+*+from+%5Bavengers%2Favengers%5D+order+by+random%28%29+limit+1"", ""label"": ""/fivethirtyeight?sql=select+*+from+%5Bavengers%2Favengers%5D+order+by+random%28%29+limit+1""}, {""href"": ""https://fivethirtyeight.datasettes.com/fivethirtyeight?sql=select+*+from+%5Bavengers%2Favengers%5D+order+by+random%28%29+limit+1&_ttl=0"", ""label"": ""/fivethirtyeight?sql=select+*+from+%5Bavengers%2Favengers%5D+order+by+random%28%29+limit+1&_ttl=0""}]"
changelog:cookie-methods,changelog,cookie-methods,Cookie methods,"Plugins can now use the new response.set_cookie() method to set cookies.
A new request.cookies method on the :ref:internals_request` can be used to read incoming cookies.","[""Changelog"", ""0.44 (2020-06-11)""]",[]
changelog:csrf-protection,changelog,csrf-protection,CSRF protection,"Since writable canned queries are built using POST forms, Datasette now ships with CSRF protection ( #798 ). This applies automatically to any POST request, which means plugins need to include a csrftoken in any POST forms that they render. They can do that like so:
","[""Changelog"", ""0.44 (2020-06-11)""]","[{""href"": ""https://github.com/simonw/datasette/issues/798"", ""label"": ""#798""}]"
changelog:csv-export,changelog,csv-export,CSV export,"Any Datasette table, view or custom SQL query can now be exported as CSV.
Check out the CSV export documentation for more details, or
try the feature out on
https://fivethirtyeight.datasettes.com/fivethirtyeight/bechdel%2Fmovies
If your table has more than max_returned_rows (default 1,000)
Datasette provides the option to stream all rows . This option takes advantage
of async Python and Datasette's efficient pagination to
iterate through the entire matching result set and stream it back as a
downloadable CSV file.","[""Changelog"", ""0.23 (2018-06-18)""]","[{""href"": ""https://fivethirtyeight.datasettes.com/fivethirtyeight/bechdel%2Fmovies"", ""label"": ""https://fivethirtyeight.datasettes.com/fivethirtyeight/bechdel%2Fmovies""}]"
changelog:documentation,changelog,documentation,Documentation,"Documentation describing how to write tests that use signed actor cookies using datasette.client.actor_cookie() . ( #1830 )
Documentation on how to register a plugin for the duration of a test . ( #2234 )
The configuration documentation now shows examples of both YAML and JSON for each setting.","[""Changelog"", ""1.0a8 (2024-02-07)""]","[{""href"": ""https://github.com/simonw/datasette/issues/1830"", ""label"": ""#1830""}, {""href"": ""https://github.com/simonw/datasette/issues/2234"", ""label"": ""#2234""}]"
changelog:facet-by-date,changelog,facet-by-date,Facet by date,"If a column contains datetime values, Datasette can now facet that column by date. ( #481 )","[""Changelog"", ""0.29 (2019-07-07)""]","[{""href"": ""https://github.com/simonw/datasette/issues/481"", ""label"": ""#481""}]"
changelog:faceting,changelog,faceting,Faceting,"The number of unique values in a facet is now always displayed. Previously it was only displayed if the user specified ?_facet_size=max . ( #1556 )
Facets of type date or array can now be configured in metadata.json , see Facets in metadata . Thanks, David Larlet. ( #1552 )
New ?_nosuggest=1 parameter for table views, which disables facet suggestion. ( #1557 )
Fixed bug where ?_facet_array=tags&_facet=tags would only display one of the two selected facets. ( #625 )","[""Changelog"", ""0.60 (2022-01-13)""]","[{""href"": ""https://github.com/simonw/datasette/issues/1556"", ""label"": ""#1556""}, {""href"": ""https://github.com/simonw/datasette/issues/1552"", ""label"": ""#1552""}, {""href"": ""https://github.com/simonw/datasette/issues/1557"", ""label"": ""#1557""}, {""href"": ""https://github.com/simonw/datasette/issues/625"", ""label"": ""#625""}]"
changelog:features,changelog,features,Features,"Now tested against Python 3.11. Docker containers used by datasette publish and datasette package both now use that version of Python. ( #1853 )
--load-extension option now supports entrypoints. Thanks, Alex Garcia. ( #1789 )
Facet size can now be set per-table with the new facet_size table metadata option. ( #1804 )
The truncate_cells_html setting now also affects long URLs in columns. ( #1805 )
The non-JavaScript SQL editor textarea now increases height to fit the SQL query. ( #1786 )
Facets are now displayed with better line-breaks in long values. Thanks, Daniel Rech. ( #1794 )
The settings.json file used in Configuration directory mode is now validated on startup. ( #1816 )
SQL queries can now include leading SQL comments, using /* ... */ or -- ... syntax. Thanks, Charles Nepote. ( #1860 )
SQL query is now re-displayed when terminated with a time limit error. ( #1819 )
The inspect data mechanism is now used to speed up server startup - thanks, Forest Gregg. ( #1834 )
In Configuration directory mode databases with filenames ending in .sqlite or .sqlite3 are now automatically added to the Datasette instance. ( #1646 )
Breadcrumb navigation display now respects the current user's permissions. ( #1831 )","[""Changelog"", ""0.63 (2022-10-27)""]","[{""href"": ""https://github.com/simonw/datasette/issues/1853"", ""label"": ""#1853""}, {""href"": ""https://github.com/simonw/datasette/pull/1789"", ""label"": ""#1789""}, {""href"": ""https://github.com/simonw/datasette/issues/1804"", ""label"": ""#1804""}, {""href"": ""https://github.com/simonw/datasette/issues/1805"", ""label"": ""#1805""}, {""href"": ""https://github.com/simonw/datasette/issues/1786"", ""label"": ""#1786""}, {""href"": ""https://github.com/simonw/datasette/pull/1794"", ""label"": ""#1794""}, {""href"": ""https://github.com/simonw/datasette/issues/1816"", ""label"": ""#1816""}, {""href"": ""https://github.com/simonw/datasette/issues/1860"", ""label"": ""#1860""}, {""href"": ""https://github.com/simonw/datasette/issues/1819"", ""label"": ""#1819""}, {""href"": ""https://github.com/simonw/datasette/issues/1834"", ""label"": ""#1834""}, {""href"": ""https://github.com/simonw/datasette/issues/1646"", ""label"": ""#1646""}, {""href"": ""https://github.com/simonw/datasette/issues/1831"", ""label"": ""#1831""}]"
changelog:flash-messages,changelog,flash-messages,Flash messages,"Writable canned queries needed a mechanism to let the user know that the query has been successfully executed. The new flash messaging system ( #790 ) allows messages to persist in signed cookies which are then displayed to the user on the next page that they visit. Plugins can use this mechanism to display their own messages, see .add_message(request, message, type=datasette.INFO) for details.
You can try out the new messages using the /-/messages debug tool, for example at https://latest.datasette.io/-/messages","[""Changelog"", ""0.44 (2020-06-11)""]","[{""href"": ""https://github.com/simonw/datasette/issues/790"", ""label"": ""#790""}, {""href"": ""https://latest.datasette.io/-/messages"", ""label"": ""https://latest.datasette.io/-/messages""}]"
changelog:foreign-key-expansions,changelog,foreign-key-expansions,Foreign key expansions,"When Datasette detects a foreign key reference it attempts to resolve a label
for that reference (automatically or using the Specifying the label column for a table metadata
option) so it can display a link to the associated row.
This expansion is now also available for JSON and CSV representations of the
table, using the new _labels=on query string option. See
Expanding foreign key references for more details.","[""Changelog"", ""0.23 (2018-06-18)""]",[]
changelog:id1,changelog,id1,Changelog,,[],[]
changelog:id10,changelog,id10,0.64.4 (2023-09-21),Fix for a crashing bug caused by viewing the table page for a named in-memory database. ( #2189 ),"[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/issues/2189"", ""label"": ""#2189""}]"
changelog:id100,changelog,id100,0.24 (2018-07-23),"A number of small new features:
datasette publish heroku now supports --extra-options , fixes #334
Custom error message if SpatiaLite is needed for specified database, closes #331
New config option: truncate_cells_html for truncating long cell values in HTML view - closes #330
Documentation for datasette publish and datasette package , closes #337
Fixed compatibility with Python 3.7
datasette publish heroku now supports app names via the -n option, which can also be used to overwrite an existing application [Russ Garrett]
Title and description metadata can now be set for canned SQL queries , closes #342
New force_https_on config option, fixes https:// API URLs when deploying to Zeit Now - closes #333
?_json_infinity=1 query string argument for handling Infinity/-Infinity values in JSON, closes #332
URLs displayed in the results of custom SQL queries are now URLified, closes #298","[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/issues/334"", ""label"": ""#334""}, {""href"": ""https://github.com/simonw/datasette/issues/331"", ""label"": ""#331""}, {""href"": ""https://github.com/simonw/datasette/issues/330"", ""label"": ""#330""}, {""href"": ""https://github.com/simonw/datasette/issues/337"", ""label"": ""#337""}, {""href"": ""https://github.com/simonw/datasette/issues/342"", ""label"": ""#342""}, {""href"": ""https://github.com/simonw/datasette/issues/333"", ""label"": ""#333""}, {""href"": ""https://github.com/simonw/datasette/issues/332"", ""label"": ""#332""}, {""href"": ""https://github.com/simonw/datasette/issues/298"", ""label"": ""#298""}]"
changelog:id109,changelog,id109,0.23.2 (2018-07-07),"Minor bugfix and documentation release.
CSV export now respects --cors , fixes #326
Installation instructions , including docker image - closes #328
Fix for row pages for tables with / in, closes #325","[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/issues/326"", ""label"": ""#326""}, {""href"": ""https://github.com/simonw/datasette/issues/328"", ""label"": ""#328""}, {""href"": ""https://github.com/simonw/datasette/issues/325"", ""label"": ""#325""}]"
changelog:id11,changelog,id11,0.64.2 (2023-03-08),"Fixed a bug with datasette publish cloudrun where deploys all used the same Docker image tag. This was mostly inconsequential as the service is deployed as soon as the image has been pushed to the registry, but could result in the incorrect image being deployed if two different deploys for two separate services ran at exactly the same time. ( #2036 )","[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/issues/2036"", ""label"": ""#2036""}]"
changelog:id113,changelog,id113,0.23.1 (2018-06-21),"Minor bugfix release.
Correctly display empty strings in HTML table, closes #314
Allow ""."" in database filenames, closes #302
404s ending in slash redirect to remove that slash, closes #309
Fixed incorrect display of compound primary keys with foreign key
references. Closes #319
Docs + example of canned SQL query using || concatenation. Closes #321
Correctly display facets with value of 0 - closes #318
Default 'expand labels' to checked in CSV advanced export","[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/issues/314"", ""label"": ""#314""}, {""href"": ""https://github.com/simonw/datasette/issues/302"", ""label"": ""#302""}, {""href"": ""https://github.com/simonw/datasette/issues/309"", ""label"": ""#309""}, {""href"": ""https://github.com/simonw/datasette/issues/319"", ""label"": ""#319""}, {""href"": ""https://github.com/simonw/datasette/issues/321"", ""label"": ""#321""}, {""href"": ""https://github.com/simonw/datasette/issues/318"", ""label"": ""#318""}]"
changelog:id12,changelog,id12,0.64.1 (2023-01-11),"Documentation now links to a current source of information for installing Python 3. ( #1987 )
Incorrectly calling the Datasette constructor using Datasette(""path/to/data.db"") instead of Datasette([""path/to/data.db""]) now returns a useful error message. ( #1985 )","[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/issues/1987"", ""label"": ""#1987""}, {""href"": ""https://github.com/simonw/datasette/issues/1985"", ""label"": ""#1985""}]"
changelog:id120,changelog,id120,0.23 (2018-06-18),"This release features CSV export, improved options for foreign key expansions,
new configuration settings and improved support for SpatiaLite.
See datasette/compare/0.22.1...0.23 for a full list of
commits added since the last release.","[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/compare/0.22.1...0.23"", ""label"": ""datasette/compare/0.22.1...0.23""}]"
changelog:id121,changelog,id121,0.22.1 (2018-05-23),"Bugfix release, plus we now use versioneer for our version numbers.
Faceting no longer breaks pagination, fixes #282
Add __version_info__ derived from __version__ [Robert Gieseke]
This might be tuple of more than two values (major and minor
version) if commits have been made after a release.
Add version number support with Versioneer. [Robert Gieseke]
Versioneer Licence:
Public Domain (CC0-1.0)
Closes #273
Refactor inspect logic [Russ Garrett]","[""Changelog""]","[{""href"": ""https://github.com/warner/python-versioneer"", ""label"": ""versioneer""}, {""href"": ""https://github.com/simonw/datasette/issues/282"", ""label"": ""#282""}, {""href"": ""https://github.com/simonw/datasette/issues/273"", ""label"": ""#273""}]"
changelog:id124,changelog,id124,0.22 (2018-05-20),"The big new feature in this release is Facets . Datasette can now apply faceted browse to any column in any table. It will also suggest possible facets. See the Datasette Facets announcement post for more details.
In addition to the work on facets:
Added docs for introspection endpoints
New --config option, added --help-config , closes #274
Removed the --page_size= argument to datasette serve in favour of:
datasette serve --config default_page_size:50 mydb.db
Added new help section:
datasette --help-config
Config options:
default_page_size Default page size for the table view
(default=100)
max_returned_rows Maximum rows that can be returned from a table
or custom query (default=1000)
sql_time_limit_ms Time limit for a SQL query in milliseconds
(default=1000)
default_facet_size Number of values to return for requested facets
(default=30)
facet_time_limit_ms Time limit for calculating a requested facet
(default=200)
facet_suggest_time_limit_ms Time limit for calculating a suggested facet
(default=50)
Only apply responsive table styles to .rows-and-column
Otherwise they interfere with tables in the description, e.g. on
https://fivethirtyeight.datasettes.com/fivethirtyeight/nba-elo%2Fnbaallelo
Refactored views into new views/ modules, refs #256
Documentation for SQLite full-text search support, closes #253
/-/versions now includes SQLite fts_versions , closes #252","[""Changelog""]","[{""href"": ""https://simonwillison.net/2018/May/20/datasette-facets/"", ""label"": ""Datasette Facets""}, {""href"": ""https://docs.datasette.io/en/stable/introspection.html"", ""label"": ""docs for introspection endpoints""}, {""href"": ""https://github.com/simonw/datasette/issues/274"", ""label"": ""#274""}, {""href"": ""https://fivethirtyeight.datasettes.com/fivethirtyeight/nba-elo%2Fnbaallelo"", ""label"": ""https://fivethirtyeight.datasettes.com/fivethirtyeight/nba-elo%2Fnbaallelo""}, {""href"": ""https://github.com/simonw/datasette/issues/256"", ""label"": ""#256""}, {""href"": ""https://docs.datasette.io/en/stable/full_text_search.html"", ""label"": ""Documentation for SQLite full-text search""}, {""href"": ""https://github.com/simonw/datasette/issues/253"", ""label"": ""#253""}, {""href"": ""https://github.com/simonw/datasette/issues/252"", ""label"": ""#252""}]"
changelog:id129,changelog,id129,0.21 (2018-05-05),"New JSON _shape= options, the ability to set table _size= and a mechanism for searching within specific columns.
Default tests to using a longer timelimit
Every now and then a test will fail in Travis CI on Python 3.5 because it hit
the default 20ms SQL time limit.
Test fixtures now default to a 200ms time limit, and we only use the 20ms time
limit for the specific test that tests query interruption. This should make
our tests on Python 3.5 in Travis much more stable.
Support _search_COLUMN=text searches, closes #237
Show version on /-/plugins page, closes #248
?_size=max option, closes #249
Added /-/versions and /-/versions.json , closes #244
Sample output:
{
""python"": {
""version"": ""3.6.3"",
""full"": ""3.6.3 (default, Oct 4 2017, 06:09:38) \n[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.37)]""
},
""datasette"": {
""version"": ""0.20""
},
""sqlite"": {
""version"": ""3.23.1"",
""extensions"": {
""json1"": null,
""spatialite"": ""4.3.0a""
}
}
}
Renamed ?_sql_time_limit_ms= to ?_timelimit , closes #242
New ?_shape=array option + tweaks to _shape , closes #245
Default is now ?_shape=arrays (renamed from lists )
New ?_shape=array returns an array of objects as the root object
Changed ?_shape=object to return the object as the root
Updated docs
FTS tables now detected by inspect() , closes #240
New ?_size=XXX query string parameter for table view, closes #229
Also added documentation for all of the _special arguments.
Plus deleted some duplicate logic implementing _group_count .
If max_returned_rows==page_size , increment max_returned_rows - fixes #230
New hidden: True option for table metadata, closes #239
Hide idx_* tables if spatialite detected, closes #228
Added class=rows-and-columns to custom query results table
Added CSS class rows-and-columns to main table
label_column option in metadata.json - closes #234","[""Changelog""]","[{""href"": ""https://github.com/simonw/datasette/issues/237"", ""label"": ""#237""}, {""href"": ""https://github.com/simonw/datasette/issues/248"", ""label"": ""#248""}, {""href"": ""https://github.com/simonw/datasette/issues/249"", ""label"": ""#249""}, {""href"": ""https://github.com/simonw/datasette/issues/244"", ""label"": ""#244""}, {""href"": ""https://github.com/simonw/datasette/issues/242"", ""label"": ""#242""}, {""href"": ""https://github.com/simonw/datasette/issues/245"", ""label"": ""#245""}, {""href"": ""https://github.com/simonw/datasette/issues/240"", ""label"": ""#240""}, {""href"": ""https://github.com/simonw/datasette/issues/229"", ""label"": ""#229""}, {""href"": ""https://github.com/simonw/datasette/issues/230"", ""label"": ""#230""}, {""href"": ""https://github.com/simonw/datasette/issues/239"", ""label"": ""#239""}, {""href"": ""https://github.com/simonw/datasette/issues/228"", ""label"": ""#228""}, {""href"": ""https://github.com/simonw/datasette/issues/234"", ""label"": ""#234""}]"
changelog:id13,changelog,id13,0.64 (2023-01-09),"Datasette now strongly recommends against allowing arbitrary SQL queries if you are using SpatiaLite . SpatiaLite includes SQL functions that could cause the Datasette server to crash. See SpatiaLite for more details.
New default_allow_sql setting, providing an easier way to disable all arbitrary SQL execution by end users: datasette --setting default_allow_sql off . See also Controlling the ability to execute arbitrary SQL . ( #1409 )
Building a location to time zone API with SpatiaLite is a new Datasette tutorial showing how to safely use SpatiaLite to create a location to time zone API.
New documentation about how to debug problems loading SQLite extensions . The error message shown when an extension cannot be loaded has also been improved. ( #1979 )
Fixed an accessibility issue: the