rowid,title,content,sections_fts,rank 793,Events,"Datasette includes a mechanism for tracking events that occur while the software is running. This is primarily intended to be used by plugins, which can both trigger events and listen for events. The core Datasette application triggers events when certain things happen. This page describes those events. Note that these events will not fire for changes made to a SQLite database by a process other than Datasette itself. Plugins can listen for events using the track_event(datasette, event) plugin hook, which will be called with instances of the following classes - or additional classes registered by other plugins . class datasette.events. LoginEvent actor : dict | None Event name: login A user (represented by event.actor ) has logged in. class datasette.events. LogoutEvent actor : dict | None Event name: logout A user (represented by event.actor ) has logged out. class datasette.events. CreateTokenEvent actor : dict | None expires_after : int | None restrict_all : list restrict_database : dict restrict_resource : dict Event name: create-token A user created an API token. Variables expires_after -- Number of seconds after which this token will expire. restrict_all -- Restricted permissions for this token. restrict_database -- Restricted database permissions for this token. restrict_resource -- Restricted resource permissions for this token. class datasette.events. CreateTableEvent actor : dict | None database : str table : str schema : str Event name: create-table A new table has been created in the database. Variables database -- The name of the database where the table was created. table -- The name of the table that was created schema -- The SQL schema definition for the new table. class datasette.events. DropTableEvent actor : dict | None database : str table : str Event name: drop-table A table has been dropped from the database. Variables database -- The name of the database where the table was dropped. table -- The name of the table that was dropped class datasette.events. AlterTableEvent actor : dict | None database : str table : str before_schema : str after_schema : str Event name: alter-table A table has been altered. Variables database -- The name of the database where the table was altered table -- The name of the table that was altered before_schema -- The table's SQL schema before the alteration after_schema -- The table's SQL schema after the alteration class datasette.events. InsertRowsEvent actor : dict | None database : str table : str num_rows : int ignore : bool replace : bool Event name: insert-rows Rows were inserted into a table. Variables database -- The name of the database where the rows were inserted. table -- The name of the table where the rows were inserted. num_rows -- The number of rows that were requested to be inserted. ignore -- Was ignore set? replace -- Was replace set? class datasette.events. UpsertRowsEvent actor : dict | None database : str table : str num_rows : int Event name: upsert-rows Rows were upserted into a table. Variables database -- The name of the database where the rows were inserted. table -- The name of the table where the rows were inserted. num_rows -- The number of rows that were requested to be inserted. class datasette.events. UpdateRowEvent actor : dict | None database : str table : str pks : list Event name: update-row A row was updated in a table. Variables database -- The name of the database where the row was updated. table -- The name of the table where the row was updated. pks -- The primary key values of the updated row. class datasette.events. RenameTableEvent actor : dict | None database : str old_table : str new_table : str Event name: rename-table A table has been renamed. Variables database -- The name of the database containing the renamed table. old_table -- The previous name of the table. new_table -- The new name of the table. class datasette.events. DeleteRowEvent actor : dict | None database : str table : str pks : list Event name: delete-row A row was deleted from a table. Variables database -- The name of the database where the row was deleted. table -- The name of the table where the row was deleted. pks -- The primary key values of the deleted row.",422, 792,Facet by date,"If Datasette finds any columns that contain dates in the first 100 values, it will offer a faceting interface against the dates of those values. This works especially well against timestamp values such as 2019-03-01 12:44:00 . Example here: latest.datasette.io/fixtures/facetable?_facet_date=created",422, 791,Facet by JSON array,"If your SQLite installation provides the json1 extension (you can check using /-/versions ) Datasette will automatically detect columns that contain JSON arrays of values and offer a faceting interface against those columns. This is useful for modelling things like tags without needing to break them out into a new table. Example here: latest.datasette.io/fixtures/facetable?_facet_array=tags",422, 790,Speeding up facets with indexes,"The performance of facets can be greatly improved by adding indexes on the columns you wish to facet by. Adding indexes can be performed using the sqlite3 command-line utility. Here's how to add an index on the state column in a table called Food_Trucks : sqlite3 mydatabase.db SQLite version 3.19.3 2017-06-27 16:48:08 Enter "".help"" for usage hints. sqlite> CREATE INDEX Food_Trucks_state ON Food_Trucks(""state""); Or using the sqlite-utils command-line utility: sqlite-utils create-index mydatabase.db Food_Trucks state",422, 789,Suggested facets,"Datasette's table UI will suggest facets for the user to apply, based on the following criteria: For the currently filtered data are there any columns which, if applied as a facet... Will return 30 or less unique options Will return more than one unique option Will return less unique options than the total number of filtered rows And the query used to evaluate this criteria can be completed in under 50ms That last point is particularly important: Datasette runs a query for every column that is displayed on a page, which could get expensive - so to avoid slow load times it sets a time limit of just 50ms for each of those queries. This means suggested facets are unlikely to appear for tables with millions of records in them.",422, 788,Facets in configuration,"You can turn facets on by default for specific tables by adding a ""facets"" key to the table configuration in datasette.yaml . See also the table configuration reference for a quick overview. Here's an example that turns on faceting by default for the qLegalStatus column in the Street_Tree_List table in the sf-trees database: [[[cog from metadata_doc import config_example config_example(cog, { ""databases"": { ""sf-trees"": { ""tables"": { ""Street_Tree_List"": { ""facets"": [""qLegalStatus""] } } } } }) ]]] [[[end]]] Facets defined in this way will always be shown in the interface and returned in the API, regardless of the _facet arguments passed to the view. Facets defined in configuration will be displayed in the order they are listed. Any additional facets added via query string parameters (e.g. ?_facet=column_name ) will appear after the configured facets, sorted by the number of unique values. You can specify array or date facets using JSON objects with a single key of array or date and a value specifying the column, like this: [[[cog config_example(cog, { ""facets"": [ {""array"": ""tags""}, {""date"": ""created""} ] }) ]]] [[[end]]] You can change the default facet size (the number of results shown for each facet) for a table using facet_size : [[[cog config_example(cog, { ""databases"": { ""sf-trees"": { ""tables"": { ""Street_Tree_List"": { ""facets"": [""qLegalStatus""], ""facet_size"": 10 } } } } }) ]]] [[[end]]]",422, 787,Facets in query strings,"To turn on faceting for specific columns on a Datasette table view, add one or more _facet=COLUMN parameters to the URL. For example, if you want to turn on facets for the city_id and state columns, construct a URL that looks like this: /dbname/tablename?_facet=state&_facet=city_id This works for both the HTML interface and the .json view. When enabled, facets will cause a facet_results block to be added to the JSON output, looking something like this: { ""state"": { ""name"": ""state"", ""results"": [ { ""value"": ""CA"", ""label"": ""CA"", ""count"": 10, ""toggle_url"": ""http://...?_facet=city_id&_facet=state&state=CA"", ""selected"": false }, { ""value"": ""MI"", ""label"": ""MI"", ""count"": 4, ""toggle_url"": ""http://...?_facet=city_id&_facet=state&state=MI"", ""selected"": false }, { ""value"": ""MC"", ""label"": ""MC"", ""count"": 1, ""toggle_url"": ""http://...?_facet=city_id&_facet=state&state=MC"", ""selected"": false } ], ""truncated"": false } ""city_id"": { ""name"": ""city_id"", ""results"": [ { ""value"": 1, ""label"": ""San Francisco"", ""count"": 6, ""toggle_url"": ""http://...?_facet=city_id&_facet=state&city_id=1"", ""selected"": false }, { ""value"": 2, ""label"": ""Los Angeles"", ""count"": 4, ""toggle_url"": ""http://...?_facet=city_id&_facet=state&city_id=2"", ""selected"": false }, { ""value"": 3, ""label"": ""Detroit"", ""count"": 4, ""toggle_url"": ""http://...?_facet=city_id&_facet=state&city_id=3"", ""selected"": false }, { ""value"": 4, ""label"": ""Memnonia"", ""count"": 1, ""toggle_url"": ""http://...?_facet=city_id&_facet=state&city_id=4"", ""selected"": false } ], ""truncated"": false } } If Datasette detects that a column is a foreign key, the ""label"" property will be automatically derived from the detected label column on the referenced table. The default number of facet results returned is 30, controlled by the default_facet_size setting. You can increase this on an individual page by adding ?_facet_size=100 to the query string, up to a maximum of max_returned_rows (which defaults to 1000).",422, 786,Facets,"Datasette facets can be used to add a faceted browse interface to any database table. With facets, tables are displayed along with a summary showing the most common values in specified columns. These values can be selected to further filter the table. Here's an example : Facets can be specified in two ways: using query string parameters, or in metadata.json configuration for the table.",422, 785,Table schema,"Use /database-name/table-name/-/schema to see the schema for a specific table. The .md and .json extensions work here too. The JSON returns an object with ""ok"" , ""database"" , ""table"" , and ""schema"" keys.",422, 784,Database schema,"Use /database-name/-/schema to see the complete schema for a specific database. The .md and .json extensions work here too. The JSON returns an object with ""ok"" , ""database"" and ""schema"" keys.",422, 783,Instance schema,"Access /-/schema to see the complete schema for all attached databases in the Datasette instance. Use /-/schema.md to get the same information as Markdown. Use /-/schema.json to get the same information as JSON, which looks like this: { ""ok"": true, ""schemas"": [ { ""database"": ""content"", ""schema"": ""create table posts ..."" } ] }",422, 782,Schemas,Datasette offers /-/schema endpoints to expose the SQL schema for databases and tables.,422, 781,Row,"Every row in every Datasette table has its own URL. This means individual records can be linked to directly. Table cells with extremely long text contents are truncated on the table view according to the truncate_cells_html setting. If a cell has been truncated the full length version of that cell will be available on the row page. Rows which are the targets of foreign key references from other tables will show a link to a filtered search for all records that reference that row. Here's an example from the Registers of Members Interests database: ../people/uk~2Eorg~2Epublicwhip~2Fperson~2F10001 Note that this URL includes the encoded primary key of the record. Here's that same page as JSON: ../people/uk~2Eorg~2Epublicwhip~2Fperson~2F10001.json",422, 780,Table fragment,"The ///-/fragment endpoint returns the rendered table HTML for rows matching the provided filters. It is used by Datasette's row editing interface to refresh rows after changes while still respecting custom table templates and render_cell plugin hooks.",422, 779,Table,"The table page is the heart of Datasette: it allows users to interactively explore the contents of a database table, including sorting, filtering, Full-text search and applying Facets . The HTML interface is worth spending some time exploring. As with other pages, you can return the JSON data by appending .json to the URL path, before any ? query string arguments. The query string arguments are described in more detail here: Table arguments You can also use the table page to interactively construct a SQL query - by applying different filters and a sort order for example - and then click the ""View and edit SQL"" link to see the SQL query that was used for the page and edit and re-submit it. Some examples: ../items lists all of the line-items registered by UK MPs as potential conflicts of interest. It demonstrates Datasette's support for Full-text search . ../antiquities-act%2Factions_under_antiquities_act is an interface for exploring the ""actions under the antiquities act"" data table published by FiveThirtyEight. ../global-power-plants?country_long=United+Kingdom&primary_fuel=Gas is a filtered table page showing every Gas power plant in the United Kingdom. It includes some default facets (configured using its metadata.json ) and uses the datasette-cluster-map plugin to show a map of the results.",422, 778,Stored query browsers,"The /-/queries page lists stored queries across every database visible to the current actor. The /database-name/-/queries page lists stored queries for a single database. The JSON versions accept ?_size= (default 50, max for the max_returned_rows limit) and a ?_next= pagination token. These pages support search, pagination and filters for read-only or writable queries and private or public queries. Adding a .json extension to either URL returns the same list as JSON.",422, 777,Write SQL queries,"The /database-name/-/execute-write page can be used to execute SQL statements that write to a mutable database, if the execute-write-sql permission is enabled. This page extracts named parameters from the SQL, shows the tables that will be affected and lists the permissions required before the query can be executed. It also includes templates for common INSERT , UPDATE and DELETE statements. Datasette checks additional permissions based on the operations in the SQL. Row changes require the relevant table-level permissions such as insert-row , update-row and delete-row ; reads from source tables require view-table ; and schema changes require permissions such as create-table , alter-table or drop-table . Use the Executing write SQL JSON API to execute writable SQL programmatically.",422, 776,Custom SQL queries,"The /database-name/-/query page can be used to execute an arbitrary SQL query against that database, if the execute-sql permission is enabled. This query is passed as the ?sql= query string parameter. This means you can link directly to a query by constructing the following URL: /database-name/-/query?sql=SELECT+*+FROM+table_name Each configured stored query has its own page, at /database-name/query-name . Viewing this page will execute the query and display the results. In both cases adding a .json extension to the URL will return the results as JSON.",422, 775,Queries,,422, 774,Hidden tables,"Some tables listed on the database page are treated as hidden. Hidden tables are not completely invisible - they can be accessed through the ""hidden tables"" link at the bottom of the page. They are hidden because they represent low-level implementation details which are generally not useful to end-users of Datasette. The following tables are hidden by default: Any table with a name that starts with an underscore - this is a Datasette convention to help plugins easily hide their own internal tables. Tables that have been configured as ""hidden"": true using hidden . *_fts tables that implement SQLite full-text search indexes. Tables relating to the inner workings of the SpatiaLite SQLite extension. sqlite_stat tables used to store statistics used by the query optimizer.",422, 773,Database,"Each database has a page listing the tables, views and stored queries available for that database. If the execute-sql permission is enabled (it's on by default) there will also be an interface for executing arbitrary SQL select queries against the data. Examples: fivethirtyeight.datasettes.com/fivethirtyeight datasette.io/global-power-plants The JSON version of this page provides programmatic access to the underlying data: fivethirtyeight.datasettes.com/fivethirtyeight.json datasette.io/global-power-plants.json The returned object includes an ""ok"": true key alongside keys such as ""database"" , ""tables"" , ""views"" , ""queries"" and ""metadata"" .",422, 772,Top-level index,"The root page of any Datasette installation is an index page that lists all of the currently attached databases. Some examples: fivethirtyeight.datasettes.com register-of-members-interests.datasettes.com Add /.json to the end of the URL for the JSON version of the underlying data: fivethirtyeight.datasettes.com/.json register-of-members-interests.datasettes.com/.json The index page can also be accessed at /-/ , useful for if the default index page has been replaced using an index.html custom template . The /-/ page will always render the default Datasette index.html template.",422, 771,Pages and API endpoints,"The Datasette web application offers a number of different pages that can be accessed to explore the data in question, each of which is accompanied by an equivalent JSON API.",422, 770,/-/messages,"The debug tool at /-/messages can be used to set flash messages to try out that feature. See .add_message(request, message, type=datasette.INFO) for details of this feature.",422, 769,/-/actor,"Shows the currently authenticated actor. Useful for debugging Datasette authentication plugins. { ""ok"": true, ""actor"": { ""id"": 1, ""username"": ""some-user"" } }",422, 768,/-/threads,"Shows details of threads and asyncio tasks. This endpoint requires the permissions-debug permission, since it exposes runtime internals. Threads example : { ""ok"": true, ""num_threads"": 2, ""threads"": [ { ""daemon"": false, ""ident"": 4759197120, ""name"": ""MainThread"" }, { ""daemon"": true, ""ident"": 123145319682048, ""name"": ""Thread-1"" }, ], ""num_tasks"": 3, ""tasks"": [ "" cb=[set.discard()]>"", "" wait_for=()]> cb=[run_until_complete..()]>"", "" wait_for=()]>>"" ] }",422, 767,/-/debug/autocomplete,"The debug tool at /-/debug/autocomplete can be used to try out the autocomplete component against a specific table. Pass ?database=db&table=table to display an autocomplete field backed by that table's /-/autocomplete endpoint. Without those query string arguments, the page lists up to five tables with detected label columns, scanning at most 100 tables.",422, 766,/-/jump,"Returns a JSON list of items that the current actor has permission to view for Datasette's jump menu. By default this includes visible databases, tables, views and stored queries, and plugins can contribute additional items. Each item includes a type string used as a category label in the menu. Items can also include an optional description with longer text describing that individual result. The endpoint supports a ?q= query parameter for filtering items by name. Jump example : { ""ok"": true, ""matches"": [ { ""name"": ""fixtures"", ""url"": ""/fixtures"", ""type"": ""database"", ""description"": null }, { ""name"": ""fixtures: facetable"", ""url"": ""/fixtures/facetable"", ""type"": ""table"", ""description"": null }, { ""name"": ""fixtures: recent_releases"", ""url"": ""/fixtures/recent_releases"", ""type"": ""query"", ""description"": null } ], ""truncated"": false } Search example with ?q=facet returns only items matching .*facet.* : { ""ok"": true, ""matches"": [ { ""name"": ""fixtures: facetable"", ""url"": ""/fixtures/facetable"", ""type"": ""table"", ""description"": null } ], ""truncated"": false } When multiple search terms are provided (e.g., ?q=user+profile ), items must match the pattern .*user.*profile.* . Results are ordered by relevance, then by item type and shortest display name.",422, 765,/-/actions,"Shows all actions registered with the permission system, including those added by plugins. Requires the permissions-debug permission. { ""ok"": true, ""actions"": [ { ""name"": ""view-instance"", ""abbr"": ""vi"", ""description"": ""View Datasette instance"", ""takes_parent"": false, ""takes_child"": false, ""resource_class"": null, ""also_requires"": null } ] }",422, 764,/-/databases,"Shows currently attached databases that the current actor is allowed to view, based on the view-database permission. Databases example : { ""ok"": true, ""databases"": [ { ""hash"": null, ""is_memory"": false, ""is_mutable"": true, ""name"": ""fixtures"", ""path"": ""fixtures.db"", ""size"": 225280 } ] }",422, 763,/-/config,"Shows the configuration for this instance of Datasette. This is generally the contents of the datasette.yaml or datasette.json file, which can include plugin configuration as well. Config example : { ""ok"": true, ""settings"": { ""template_debug"": true, ""trace_debug"": true, ""force_https_urls"": true } } Any keys that include the one of the following substrings in their names will be returned as redacted *** output, to help avoid accidentally leaking private configuration information: secret , key , password , token , hash , dsn .",422, 762,/-/settings,"Shows the Settings for this instance of Datasette. Settings example : { ""ok"": true, ""default_facet_size"": 30, ""default_page_size"": 100, ""facet_suggest_time_limit_ms"": 50, ""facet_time_limit_ms"": 1000, ""max_returned_rows"": 1000, ""sql_time_limit_ms"": 1000 }",422, 761,/-/plugins,"Shows a list of currently installed plugins and their versions. Plugins example : [ { ""name"": ""datasette_cluster_map"", ""static"": true, ""templates"": false, ""version"": ""0.10"", ""hooks"": [""extra_css_urls"", ""extra_js_urls"", ""extra_body_script""] } ] Add ?all=1 to include details of the default plugins baked into Datasette.",422, 760,/-/versions,"Shows the version of Datasette, Python and SQLite. Versions example : { ""ok"": true, ""datasette"": { ""version"": ""0.60"" }, ""python"": { ""full"": ""3.8.12 (default, Dec 21 2021, 10:45:09) \n[GCC 10.2.1 20210110]"", ""version"": ""3.8.12"" }, ""sqlite"": { ""extensions"": { ""json1"": null }, ""fts_versions"": [ ""FTS5"", ""FTS4"", ""FTS3"" ], ""compile_options"": [ ""COMPILER=gcc-6.3.0 20170516"", ""ENABLE_FTS3"", ""ENABLE_FTS4"", ""ENABLE_FTS5"", ""ENABLE_JSON1"", ""ENABLE_RTREE"", ""THREADSAFE=1"" ], ""version"": ""3.37.0"" } }",422, 759,/-/metadata,"Shows the contents of the metadata.json file that was passed to datasette serve , if any. Metadata example : { ""license"": ""CC Attribution 4.0 License"", ""license_url"": ""http://creativecommons.org/licenses/by/4.0/"", ""source"": ""fivethirtyeight/data on GitHub"", ""source_url"": ""https://github.com/fivethirtyeight/data"", ""title"": ""Five Thirty Eight"", ""databases"": { } }",422, 758,Introspection,"Datasette includes some pages and JSON API endpoints for introspecting the current instance. These can be used to understand some of the internals of Datasette and to see how a particular instance has been configured. Each of these pages can be viewed in your browser. Add .json to the URL to get back the contents as JSON. JSON responses that return an object include an ""ok"": true key, consistent with the rest of the JSON API . The introspection endpoints documented on this page are covered by the JSON API stability promise , with the exception of the debug endpoints /-/threads and /-/actions , whose shapes may change in future releases.",422, 757,Using secrets with datasette publish,"The datasette publish and datasette package commands both generate a secret for you automatically when Datasette is deployed. This means that every time you deploy a new version of a Datasette project, a new secret will be generated. This will cause signed cookies to become invalid on every fresh deploy. You can fix this by creating a secret that will be used for multiple deploys and passing it using the --secret option: datasette publish cloudrun mydb.db --service=my-service --secret=cdb19e94283a20f9d42cca5",422, 756,Configuring the secret,"Datasette uses a secret string to sign secure values such as cookies. If you do not provide a secret, Datasette will create one when it starts up. This secret will reset every time the Datasette server restarts though, so things like authentication cookies and API tokens will not stay valid between restarts. You can pass a secret to Datasette in two ways: with the --secret command-line option or by setting a DATASETTE_SECRET environment variable. datasette mydb.db --secret=SECRET_VALUE_HERE Or: export DATASETTE_SECRET=SECRET_VALUE_HERE datasette mydb.db One way to generate a secure random secret is to use Python like this: python3 -c 'import secrets; print(secrets.token_hex(32))' cdb19e94283a20f9d42cca50c5a4871c0aa07392db308755d60a1a5b9bb0fa52 Plugin authors can make use of this signing mechanism in their plugins using the datasette.sign() and datasette.unsign() methods.",422, 755,base_url,"If you are running Datasette behind a proxy, it may be useful to change the root path used for the Datasette instance. For example, if you are sending traffic from https://www.example.com/tools/datasette/ through to a proxied Datasette instance you may wish Datasette to use /tools/datasette/ as its root URL. You can do that like so: datasette mydatabase.db --setting base_url /tools/datasette/",422, 754,trace_debug,"This setting enables appending ?_trace=1 to any page in order to see the SQL queries and other trace information that was used to generate that page. Enable it like this: datasette mydatabase.db --setting trace_debug 1 Some examples: https://latest.datasette.io/?_trace=1 https://latest.datasette.io/fixtures/roadside_attractions?_trace=1 See JSON encoding for details on how to hook into this mechanism as a plugin author.",422, 753,template_debug,"This setting enables template context debug mode, which is useful to help understand what variables are available to custom templates when you are writing them. Enable it like this: datasette mydatabase.db --setting template_debug 1 Now you can add ?_context=1 or &_context=1 to any Datasette page to see the context that was passed to that template. Some examples: https://latest.datasette.io/?_context=1 https://latest.datasette.io/fixtures?_context=1 https://latest.datasette.io/fixtures/roadside_attractions?_context=1",422, 752,force_https_urls,"Forces self-referential URLs in the JSON output to always use the https:// protocol. This is useful for cases where the application itself is hosted using HTTP but is served to the outside world via a proxy that enables HTTPS. datasette mydatabase.db --setting force_https_urls 1",422, 751,truncate_cells_html,"In the HTML table view, truncate any strings that are longer than this value. The full value will still be available in CSV, JSON and on the individual row HTML page. Set this to 0 to disable truncation. datasette mydatabase.db --setting truncate_cells_html 0",422, 750,max_csv_mb,"The maximum size of CSV that can be exported, in megabytes. Defaults to 100MB. You can disable the limit entirely by settings this to 0: datasette mydatabase.db --setting max_csv_mb 0",422, 749,allow_csv_stream,"Enables the CSV export feature where an entire table (potentially hundreds of thousands of rows) can be exported as a single CSV file. This is turned on by default - you can turn it off like this: datasette mydatabase.db --setting allow_csv_stream off",422, 748,cache_size_kb,"Sets the amount of memory SQLite uses for its per-connection cache , in KB. datasette mydatabase.db --setting cache_size_kb 5000",422, 747,default_cache_ttl,"Default HTTP caching max-age header in seconds, used for Cache-Control: max-age=X . Can be over-ridden on a per-request basis using the ?_ttl= query string parameter. Set this to 0 to disable HTTP caching entirely. Defaults to 5 seconds. datasette mydatabase.db --setting default_cache_ttl 60",422, 746,max_signed_tokens_ttl,"Maximum allowed expiry time for signed API tokens created by users. Defaults to 0 which means no limit - tokens can be created that will never expire. Set this to a value in seconds to limit the maximum expiry time. For example, to set that limit to 24 hours you would use: datasette mydatabase.db --setting max_signed_tokens_ttl 86400 This setting is enforced when incoming tokens are processed.",422, 745,allow_signed_tokens,"Should users be able to create signed API tokens to access Datasette? This is turned on by default. Use the following to turn it off: datasette mydatabase.db --setting allow_signed_tokens off Turning this setting off will disable the /-/create-token page, described here . It will also cause any incoming Authorization: Bearer dstok_... API tokens to be ignored.",422, 744,allow_download,"Should users be able to download the original SQLite database using a link on the database index page? This is turned on by default. However, databases can only be downloaded if they are served in immutable mode and not in-memory. If downloading is unavailable for either of these reasons, the download link is hidden even if allow_download is on. To disable database downloads, use the following: datasette mydatabase.db --setting allow_download off",422, 743,suggest_facets,"Should Datasette calculate suggested facets? On by default, turn this off like so: datasette mydatabase.db --setting suggest_facets off",422, 742,facet_suggest_time_limit_ms,"When Datasette calculates suggested facets it needs to run a SQL query for every column in your table. The default for this time limit is 50ms to account for the fact that it needs to run once for every column. If the time limit is exceeded the column will not be suggested as a facet. You can increase this time limit like so: datasette mydatabase.db --setting facet_suggest_time_limit_ms 500",422, 741,facet_time_limit_ms,"This is the time limit Datasette allows for calculating a facet, which defaults to 200ms: datasette mydatabase.db --setting facet_time_limit_ms 1000",422, 740,default_facet_size,"The default number of unique rows returned by Facets is 30. You can customize it like this: datasette mydatabase.db --setting default_facet_size 50",422, 739,allow_facet,"Allow users to specify columns they would like to facet on using the ?_facet=COLNAME URL parameter to the table view. This is enabled by default. If disabled, facets will still be displayed if they have been specifically enabled in metadata.json configuration for the table. Here's how to disable this feature: datasette mydatabase.db --setting allow_facet off",422, 738,num_sql_threads,"Maximum number of threads in the thread pool Datasette uses to execute SQLite queries. Defaults to 3. datasette mydatabase.db --setting num_sql_threads 10 Setting this to 0 turns off threaded SQL queries entirely - useful for environments that do not support threading such as Pyodide .",422, 737,max_post_body_bytes,"Maximum size in bytes for a POST body that Datasette reads fully into memory, such as JSON submitted to the write API . Requests with larger bodies are rejected with an HTTP 413 error. Defaults to 2,097,152 (2MB). This limit exists to protect against memory exhaustion: unlike file uploads handled by request.form() , which stream to disk, these bodies are held entirely in memory and parsing them as JSON can multiply their memory footprint several times over. If you increase max_insert_rows to support larger bulk inserts you may need to increase this limit as well: datasette mydatabase.db --setting max_post_body_bytes 10485760 Set it to 0 to disable the limit entirely: datasette mydatabase.db --setting max_post_body_bytes 0",422, 736,max_insert_rows,"Maximum rows that can be inserted at a time using the bulk insert API, see Inserting rows . Defaults to 100. You can increase or decrease this limit like so: datasette mydatabase.db --setting max_insert_rows 1000",422, 735,max_returned_rows,"Datasette returns a maximum of 1,000 rows of data at a time. If you execute a query that returns more than 1,000 rows, Datasette will return the first 1,000 and include a warning that the result set has been truncated. You can use OFFSET/LIMIT or other methods in your SQL to implement pagination if you need to return more than 1,000 rows. You can increase or decrease this limit like so: datasette mydatabase.db --setting max_returned_rows 2000",422, 734,sql_time_limit_ms,"By default, queries have a time limit of one second. If a query takes longer than this to run Datasette will terminate the query and return an error. If this time limit is too short for you, you can customize it using the sql_time_limit_ms limit - for example, to increase it to 3.5 seconds: datasette mydatabase.db --setting sql_time_limit_ms 3500 You can optionally set a lower time limit for an individual query using the ?_timelimit=100 query string argument: /my-database/my-table?qSpecies=44&_timelimit=100 This would set the time limit to 100ms for that specific query. This feature is useful if you are working with databases of unknown size and complexity - a query that might make perfect sense for a smaller table could take too long to execute on a table with millions of rows. By setting custom time limits you can execute queries ""optimistically"" - e.g. give me an exact count of rows matching this query but only if it takes less than 100ms to calculate.",422, 733,default_page_size,"The default number of rows returned by the table page. You can over-ride this on a per-page basis using the ?_size=80 query string parameter, provided you do not specify a value higher than the max_returned_rows setting. You can set this default using --setting like so: datasette mydatabase.db --setting default_page_size 50",422, 732,default_allow_sql,"Should users be able to execute arbitrary SQL queries by default? Setting this to off causes permission checks for execute-sql to fail by default. datasette mydatabase.db --setting default_allow_sql off Another way to achieve this is to add ""allow_sql"": false to your datasette.yaml file, as described in Controlling the ability to execute arbitrary SQL . This setting offers a more convenient way to do this.",422, 731,Settings,"The following options can be set using --setting name value , or by storing them in the settings.json file for use with Configuration directory mode .",422, 730,Configuration directory mode,"Normally you configure Datasette using command-line options. For a Datasette instance with custom templates, custom plugins, a static directory and several databases this can get quite verbose: datasette one.db two.db \ --metadata=metadata.json \ --template-dir=templates/ \ --plugins-dir=plugins \ --static css:css As an alternative to this, you can run Datasette in configuration directory mode. Create a directory with the following structure: # In a directory called my-app: my-app/one.db my-app/two.db my-app/datasette.yaml my-app/metadata.json my-app/templates/index.html my-app/plugins/my_plugin.py my-app/static/my.css Now start Datasette by providing the path to that directory: datasette my-app/ Datasette will detect the files in that directory and automatically configure itself using them. It will serve all *.db files that it finds, will load metadata.json if it exists, and will load the templates , plugins and static folders if they are present. The files that can be included in this directory are as follows. All are optional. *.db (or *.sqlite3 or *.sqlite ) - SQLite database files that will be served by Datasette datasette.yaml - Configuration for the Datasette instance metadata.json - Metadata for those databases - metadata.yaml or metadata.yml can be used as well inspect-data.json - the result of running datasette inspect *.db --inspect-file=inspect-data.json from the configuration directory - any database files listed here will be treated as immutable, so they should not be changed while Datasette is running templates/ - a directory containing Custom templates plugins/ - a directory containing plugins, see Writing one-off plugins static/ - a directory containing static files - these will be served from /static/filename.txt , see Linking to static assets",422, 729,Using --setting,"Datasette supports a number of settings. These can be set using the --setting name value option to datasette serve . You can set multiple settings at once like this: datasette mydatabase.db \ --setting default_page_size 50 \ --setting sql_time_limit_ms 3500 \ --setting max_returned_rows 2000 Settings can also be specified in the database.yaml configuration file .",422, 728,Settings,,422, 727,,"You can assign semantic column types to columns, which affect how values are rendered, validated, transformed, and edited. Built-in column types include url , email , json , and textarea . Plugins can register additional column types using the register_column_types plugin hook. Column types can optionally declare which SQLite column types they apply to using sqlite_types . Datasette will reject incompatible assignments. The built-in url , email , json , and textarea column types are all restricted to TEXT columns. The simplest form maps column names to type name strings: [[[cog config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: column_types: website: url contact: email extra_data: json notes: textarea """""").strip() ) ]]] [[[end]]] For column types that accept additional configuration, use an object with type and config keys: [[[cog config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: column_types: website: type: url config: prefix: ""https://"" """""").strip() ) ]]] [[[end]]]",422, 726,,"These configure full-text search for a table or view. See Configuring full-text search for a table or view for full details. fts_table specifies which FTS table to use for search. fts_pk sets the primary key column if it is something other than rowid . searchmode can be set to ""raw"" to enable SQLite advanced search operators . [[[cog config_example(cog, textwrap.dedent( """""" databases: russian-ads: tables: display_ads: fts_table: ads_fts fts_pk: id searchmode: raw """""").strip() ) ]]] [[[end]]]",422, 725,,"You can turn on facets by default for specific tables. facet_size controls how many unique values are shown for each facet on that table (the default is controlled by the default_facet_size setting). See Facets in configuration for full details. [[[cog config_example(cog, textwrap.dedent( """""" databases: sf-trees: tables: Street_Tree_List: facets: - qLegalStatus facet_size: 10 """""").strip() ) ]]] [[[end]]] You can also specify array or date facets using JSON objects with a single key of array or date : facets: - array: tags - date: created",422, 724,,"You can hide tables from the database listing view (in the same way that FTS and SpatiaLite tables are automatically hidden) using ""hidden"": true : [[[cog config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: hidden: true """""").strip() ) ]]] [[[end]]]",422, 723,,"Datasette's HTML interface attempts to display foreign key references as labelled hyperlinks. By default, it automatically detects a label column using the following rules (in order): If there is exactly one unique text column, use that. If there is a column called name or title (case-insensitive), use that. If the table has only two columns - a primary key and one other - use the non-primary-key column. You can override this automatic detection by specifying which column should be used for the link label with the label_column property: [[[cog config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: label_column: title """""").strip() ) ]]] [[[end]]]",422, 722,,"Datasette allows any column to be used for sorting by default. If you need to control which columns are available for sorting you can do so using sortable_columns : [[[cog config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: sortable_columns: - height - weight """""").strip() ) ]]] [[[end]]] This will restrict sorting of example_table to just the height and weight columns. You can also disable sorting entirely by setting ""sortable_columns"": [] You can use sortable_columns to enable specific sort orders for a view called name_of_view in the database my_database like so: [[[cog config_example(cog, textwrap.dedent( """""" databases: my_database: tables: name_of_view: sortable_columns: - clicks - impressions """""").strip() ) ]]] [[[end]]]",422, 721,,"Datasette defaults to displaying 100 rows per page, for both tables and views. You can change this on a per-table or per-view basis using the size key: [[[cog config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: size: 10 """""").strip() ) ]]] [[[end]]] This size can still be over-ridden by passing e.g. ?_size=50 in the query string.",422, 720,,"By default Datasette tables are sorted by primary key. You can set a default sort order for a specific table using the sort or sort_desc properties: [[[cog from metadata_doc import config_example import textwrap config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: sort: created """""").strip() ) ]]] [[[end]]] Or use sort_desc to sort in descending order: [[[cog config_example(cog, textwrap.dedent( """""" databases: mydatabase: tables: example_table: sort_desc: created """""").strip() ) ]]] [[[end]]]",422, 719,Table configuration,Datasette supports a number of table-level configuration options inside datasette.yaml . These are placed under databases.database_name.tables.table_name .,422, 718,Custom CSS and JavaScript,"Datasette can load additional CSS and JavaScript files, configured in datasette.yaml like this: [[[cog from metadata_doc import config_example config_example(cog, """""" extra_css_urls: - https://simonwillison.net/static/css/all.bf8cd891642c.css extra_js_urls: - https://code.jquery.com/jquery-3.2.1.slim.min.js """""") ]]] [[[end]]] The extra CSS and JavaScript files will be linked in the of every page: You can also specify a SRI (subresource integrity hash) for these assets: [[[cog config_example(cog, """""" extra_css_urls: - url: https://simonwillison.net/static/css/all.bf8cd891642c.css sri: sha384-9qIZekWUyjCyDIf2YK1FRoKiPJq4PHt6tp/ulnuuyRBvazd0hG7pWbE99zvwSznI extra_js_urls: - url: https://code.jquery.com/jquery-3.2.1.slim.min.js sri: sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g= """""") ]]] [[[end]]] This will produce: Modern browsers will only execute the stylesheet or JavaScript if the SRI hash matches the content served. You can generate hashes using www.srihash.org Items in ""extra_js_urls"" can specify ""module"": true if they reference JavaScript that uses JavaScript modules . This configuration: [[[cog config_example(cog, """""" extra_js_urls: - url: https://example.datasette.io/module.js module: true """""") ]]] [[[end]]] Will produce this HTML: ",422, 717,Queries configuration,"Queries are named SQL queries that appear in the Datasette interface. They can be configured in datasette.yaml using the queries key at the database level: [[[cog from metadata_doc import config_example, config_example config_example(cog, { ""databases"": { ""sf-trees"": { ""queries"": { ""just_species"": { ""sql"": ""select qSpecies from Street_Tree_List"" } } } } }) ]]] [[[end]]] See the queries documentation for more, including how to configure writable queries .",422, 716,Permissions configuration,"Datasette's authentication and permissions system can also be configured using datasette.yaml . Here is a simple example: [[[cog from metadata_doc import config_example import textwrap config_example(cog, textwrap.dedent( """""" # Instance is only available to users 'sharon' and 'percy': allow: id: - sharon - percy # Only 'percy' is allowed access to the accounting database: databases: accounting: allow: id: percy """""").strip() ) ]]] [[[end]]] Access permissions in datasette.yaml has the full details.",422, 715,Plugin configuration,"Datasette plugins often require configuration. This plugin configuration should be placed in plugins keys inside datasette.yaml . Most plugins are configured at the top-level of the file, using the plugins key: [[[cog from metadata_doc import config_example import textwrap config_example(cog, textwrap.dedent( """""" # inside datasette.yaml plugins: datasette-my-plugin: key: my_value """""").strip() ) ]]] [[[end]]] Some plugins can be configured at the database or table level. These should use a plugins key nested under the appropriate place within the databases object: [[[cog from metadata_doc import config_example import textwrap config_example(cog, textwrap.dedent( """""" # inside datasette.yaml databases: my_database: # plugin configuration for the my_database database plugins: datasette-my-plugin: key: my_value my_other_database: tables: my_table: # plugin configuration for the my_table table inside the my_other_database database plugins: datasette-my-plugin: key: my_value """""").strip() ) ]]] [[[end]]]",422, 714,Settings,"Settings can be configured in datasette.yaml with the settings key: [[[cog from metadata_doc import config_example import textwrap config_example(cog, textwrap.dedent( """""" # inside datasette.yaml settings: default_allow_sql: off default_page_size: 50 """""").strip() ) ]]] [[[end]]] The full list of settings is available in the settings documentation . Settings can also be passed to Datasette using one or more --setting name value command line options.`",422, 713,,"The following example shows some of the valid configuration options that can exist inside datasette.yaml . [[[cog from metadata_doc import config_example import textwrap config_example(cog, textwrap.dedent( """""" # Datasette settings block settings: default_page_size: 50 sql_time_limit_ms: 3500 max_returned_rows: 2000 # top-level plugin configuration plugins: datasette-my-plugin: key: valueA # Database and table-level configuration databases: your_db_name: # plugin configuration for the your_db_name database plugins: datasette-my-plugin: key: valueA tables: your_table_name: allow: # Only the root user can access this table id: root # plugin configuration for the your_table_name table # inside your_db_name database plugins: datasette-my-plugin: key: valueB """""") ) ]]] [[[end]]]",422, 712,Configuration via the command-line,"The recommended way to configure Datasette is using a datasette.yaml file passed to -c/--config . You can also pass individual settings to Datasette using the -s/--setting option, which can be used multiple times: datasette mydatabase.db \ --setting settings.default_page_size 50 \ --setting settings.sql_time_limit_ms 3500 This option takes dotted-notation for the first argument and a value for the second argument. This means you can use it to set any configuration value that would be valid in a datasette.yaml file. It also works for plugin configuration, for example for datasette-cluster-map : datasette mydatabase.db \ --setting plugins.datasette-cluster-map.latitude_column xlat \ --setting plugins.datasette-cluster-map.longitude_column xlon If the value you provide is a valid JSON object or list it will be treated as nested data, allowing you to configure plugins that accept lists such as datasette-proxy-url : datasette mydatabase.db \ -s plugins.datasette-proxy-url.paths '[{""path"": ""/proxy"", ""backend"": ""http://example.com/""}]' This is equivalent to a datasette.yaml file containing the following: [[[cog from metadata_doc import config_example import textwrap config_example(cog, textwrap.dedent( """""" plugins: datasette-proxy-url: paths: - path: /proxy backend: http://example.com/ """""").strip() ) ]]] [[[end]]]",422, 711,Configuration,"Datasette offers several ways to configure your Datasette instances: server settings, plugin configuration, authentication, and more. Most configuration can be handled using a datasette.yaml configuration file, passed to datasette using the -c/--config flag: datasette mydatabase.db --config datasette.yaml This file can also use JSON, as datasette.json . YAML is recommended over JSON due to its support for comments and multi-line strings.",422, 710,Contents,"Getting started Play with a live demo Follow a tutorial Datasette in your browser with Datasette Lite Try Datasette without installing anything with Codespaces Using Datasette on your own computer Installation Basic installation Using Homebrew Using pip Advanced installation options Using pipx Using Docker A note about extensions Configuration Configuration via the command-line datasette.yaml reference Settings Plugin configuration Permissions configuration Queries configuration Custom CSS and JavaScript Table configuration The Datasette Ecosystem sqlite-utils Dogsheep CLI reference datasette --help datasette serve Environment variables datasette --get datasette serve --help-settings datasette plugins datasette install datasette uninstall datasette publish datasette publish cloudrun datasette publish heroku datasette package datasette inspect datasette create-token Pages and API endpoints Top-level index Database Hidden tables Queries Custom SQL queries Write SQL queries Stored query browsers Table Table fragment Row Schemas Instance schema Database schema Table schema Publishing data datasette publish Publishing to Google Cloud Run Publishing to Heroku Publishing to Vercel Publishing to Fly Custom metadata and plugins datasette package Deploying Datasette Deployment fundamentals Running Datasette using systemd Running Datasette using OpenRC Deploying using buildpacks Running Datasette behind a proxy Nginx proxy configuration Apache proxy configuration JSON API API stability Default representation Error responses Executing custom SQL Different shapes Pagination Special JSON arguments Expanding JSON responses Table JSON responses Row JSON responses Query JSON responses Table autocomplete Table arguments Column filter arguments Special table arguments Expanding foreign key references Discovering the JSON for a page Enabling CORS The JSON write API Executing write SQL Inserting rows Upserting rows Updating a row Deleting a row Creating a table Creating a table from example data Database foreign key targets Table foreign key suggestions Altering tables Setting a column type Dropping tables Running SQL queries Named parameters Views Queries Stored queries Query parameters Additional query options Writable queries Magic parameters JSON API for writable queries Pagination Cross-database queries Authentication and permissions Actors How actors are displayed Using the ""root"" actor Permissions Denying all permissions by default How permissions are resolved Defining permissions with ""allow"" blocks The /-/allow-debug tool Access permissions in datasette.yaml Access to an instance Access to specific databases Access to specific tables and views Access to specific queries Controlling the ability to execute arbitrary SQL Other permissions in datasette.yaml API Tokens datasette create-token Checking permissions in plugins actor_matches_allow() Permissions debug tools Allowed resources view Permission rules view Permission check view The ds_actor cookie Including an expiry time The /-/logout page Built-in actions view-instance view-database view-database-download view-table view-query store-query update-query delete-query insert-row delete-row update-row create-table create-view alter-table set-column-type drop-table drop-view execute-sql execute-write-sql permissions-debug debug-menu Performance and caching Immutable mode Using ""datasette inspect"" HTTP caching datasette-hashed-urls CSV export URL parameters Streaming all records Binary data Binary values in JSON Linking to binary downloads Binary plugins Facets Facets in query strings Facets in configuration Suggested facets Speeding up facets with indexes Facet by JSON array Facet by date Full-text search The table page and table view API Advanced SQLite search queries Configuring full-text search for a table or view Searches using custom SQL Enabling full-text search for a SQLite table Configuring FTS using sqlite-utils Configuring FTS using csvs-to-sqlite Configuring FTS by hand FTS versions SpatiaLite Warning Installation Installing SpatiaLite on OS X Installing SpatiaLite on Linux Spatial indexing latitude/longitude columns Making use of a spatial index Importing shapefiles into SpatiaLite Importing GeoJSON polygons using Shapely Querying polygons using within() Metadata Per-database and per-table metadata Source, license and about Column descriptions Table configuration Metadata reference Top-level metadata Database-level metadata Table-level metadata Settings Using --setting Configuration directory mode Settings default_allow_sql default_page_size sql_time_limit_ms max_returned_rows max_insert_rows max_post_body_bytes num_sql_threads allow_facet default_facet_size facet_time_limit_ms facet_suggest_time_limit_ms suggest_facets allow_download allow_signed_tokens max_signed_tokens_ttl default_cache_ttl cache_size_kb allow_csv_stream max_csv_mb truncate_cells_html force_https_urls template_debug trace_debug base_url Configuring the secret Using secrets with datasette publish Introspection /-/metadata /-/versions /-/plugins /-/settings /-/config /-/databases /-/actions /-/jump /-/debug/autocomplete /-/threads /-/actor /-/messages Custom pages and templates CSS classes on the Writing custom CSS Linking to static assets Serving files from a directory Publishing static assets Custom templates Custom pages Path parameters for pages Custom headers and status codes Returning 404s Custom redirects Custom error pages Template context Base context Database page Query page Table page Row page Plugins Installing plugins One-off plugins using --plugins-dir Deploying plugins using datasette publish Controlling which plugins are loaded Seeing what plugins are installed Plugin configuration Secret configuration values Writing plugins Tracing plugin hooks Writing one-off plugins Starting an installable plugin using cookiecutter Packaging a plugin Static assets Custom templates Writing plugins that accept configuration Designing URLs for your plugin Building URLs within plugins Plugins that define new plugin hooks JavaScript plugins The datasette_init event datasetteManager JavaScript plugin objects makeJumpSections(context) makeAboveTablePanelConfigs() makeColumnActions(columnDetails) makeColumnField(context) Selectors Plugin hooks prepare_connection(conn, database, datasette) write_wrapper(datasette, database, request, transaction) prepare_jinja2_environment(env, datasette) Page extras extra_template_vars(template, database, table, columns, view_name, request, datasette) extra_css_urls(template, database, table, columns, view_name, request, datasette) extra_js_urls(template, database, table, columns, view_name, request, datasette) extra_body_script(template, database, table, columns, view_name, request, datasette) publish_subcommand(publish) render_cell(row, value, column, table, pks, database, datasette, request, column_type) register_output_renderer(datasette) register_routes(datasette) register_commands(cli) register_facet_classes() register_actions(datasette) The resources_sql(datasette, actor) method register_column_types(datasette) asgi_wrapper(datasette) startup(datasette) actor_from_request(datasette, request) actors_from_ids(datasette, actor_ids) jinja2_environment_from_request(datasette, request, env) filters_from_request(request, database, table, datasette) permission_resources_sql(datasette, actor, action) Permission plugin examples register_magic_parameters(datasette) forbidden(datasette, request, message) handle_exception(datasette, request, exception) menu_links(datasette, actor, request) jump_items_sql(datasette, actor, request) Action hooks table_actions(datasette, actor, database, table, request) view_actions(datasette, actor, database, view, request) query_actions(datasette, actor, database, query_name, request, sql, params) row_actions(datasette, actor, request, database, table, row) database_actions(datasette, actor, database, request) homepage_actions(datasette, actor, request) Template slots top_homepage(datasette, request) top_database(datasette, request, database) top_table(datasette, request, database, table) top_row(datasette, request, database, table, row) top_query(datasette, request, database, sql) top_stored_query(datasette, request, database, query_name) Event tracking track_event(datasette, event) register_events(datasette) register_token_handler(datasette) Testing plugins Setting up a Datasette test instance Using Datasette's fixtures database Automatic cleanup of Datasette instances Using datasette.client in tests Using pdb for errors thrown inside Datasette Using pytest fixtures Testing outbound HTTP calls with pytest-httpx Registering a plugin for the duration of a test Internals for plugins Request object The MultiParams class The FormData class The UploadedFile class Response class Returning a response with .asgi_send(send) Setting cookies with response.set_cookie() Datasette class .databases .actions .plugin_config(plugin_name, database=None, table=None) await .render_template(template, context=None, request=None) .static(path, plugin=None, mount=None) await .actors_from_ids(actor_ids) await .allowed(*, action, resource, actor=None) await .allowed_many(*, actions, resource, actor=None) await .allowed_resources(action, actor=None, *, parent=None, include_is_private=False, include_reasons=False, limit=100, next=None) await .allowed_resources_sql(*, action, actor=None, parent=None, include_is_private=False) await .ensure_permission(action, resource=None, actor=None) await .check_visibility(actor, action, resource=None) await .create_token(actor_id, expires_after=None, restrictions=None, handler=None) await .verify_token(token) .get_database(name) .get_internal_database() Getting and setting metadata Stored queries Column types .add_database(db, name=None, route=None) .add_memory_database(memory_name, name=None, route=None) .remove_database(name) .close() await .track_event(event) .sign(value, namespace=""default"") .unsign(value, namespace=""default"") .add_message(request, message, type=datasette.INFO) .absolute_url(request, path) .setting(key) .resolve_database(request) .resolve_table(request) .resolve_row(request) datasette.client datasette.urls Permission classes and utilities PermissionSQL class Database class Database(ds, path=None, is_mutable=True, is_memory=False, memory_name=None, is_temp_disk=False) db.hash await db.execute(sql, ...) Results await db.execute_fn(fn) await db.execute_write(sql, params=None, block=True, request=None, return_all=False, returning_limit=10, transaction=True) await db.execute_write_script(sql, block=True) await db.execute_write_many(sql, params_seq, block=True) await db.execute_write_fn(fn, block=True, transaction=True) await db.execute_isolated_fn(fn) db.close() Database introspection CSRF protection Datasette's internal database Internal database schema The datasette.utils module parse_metadata(content) await_me_maybe(value) named_parameters(sql) Tilde encoding call_with_supported_arguments(fn, **kwargs) await async_call_with_supported_arguments(fn, **kwargs) JSON encoding datasette.tracer Tracing child tasks Import shortcuts Events LoginEvent LogoutEvent CreateTokenEvent CreateTableEvent DropTableEvent AlterTableEvent InsertRowsEvent UpsertRowsEvent UpdateRowEvent RenameTableEvent DeleteRowEvent Upgrade guide Datasette 0.X -> 1.0 New URL for SQL queries Metadata changes Datasette 1.0a20 plugin upgrade guide Permissions are now actions permission_allowed() hook is replaced by permission_resources_sql() Using datasette.allowed() to check permissions instead of datasette.permission_allowed() Root user checks are no longer necessary Target the new APIs exclusively Fixing async with httpx.AsyncClient(app=app) Migrating from metadata= to config= Datasette 1.0a25: create_token() signature change CSRF protection is now header-based Contributing General guidelines Setting up a development environment Running the tests Running Playwright tests Using fixtures Debugging Code formatting Running Black blacken-docs Prettier Editing and building the documentation Running Cog Documented template contexts Continuously deployed demo instances Release process Alpha and beta releases Releasing bug fixes from a branch Upgrading CodeMirror Changelog 1.0a38 (2026-08-06) 1.0a37 (2026-07-14) 1.0a36 (2026-07-07) JSON API: breaking changes JSON API: other improvements 1.0a35 (2026-06-23) 1.0a34 (2026-06-16) 1.0a33 (2026-06-11) Editing and deleting stored queries ?_extra= support for row and query pages Security fixes Bug fixes 1.0a32 (2026-05-31) 1.0a31 (2026-05-28) Write SQL UI Stored queries Plugin API changes Bug fixes 1.0a30 (2026-05-24) 1.0a29 (2026-05-12) 1.0a28 (2026-04-16) 1.0a27 (2026-04-15) CSRF protection no longer uses CSRF tokens RenameTableEvent when a table is renamed Other changes 1.0a26 (2026-03-18) New column_types system UI for selecting columns and their order Other changes 1.0a25 (2026-02-25) write_wrapper() plugin hook for intercepting write operations register_token_handler() plugin hook for custom API token backends render_cell() now receives a pks parameter Other changes 1.0a24 (2026-01-29) request.form() method for POST data and file uploads render_cell and foreign_key_tables extras for the JSON API uv run pytest with a dev= dependency group Other changes 1.0a23 (2025-12-02) 1.0a22 (2025-11-13) 1.0a21 (2025-11-05) 0.65.2 (2025-11-05) 1.0a20 (2025-11-03) Permission system redesign Other changes 1.0a19 (2025-04-21) 1.0a18 (2025-04-16) 1.0a17 (2025-02-06) 0.65.1 (2024-11-28) 0.65 (2024-10-07) 1.0a16 (2024-09-05) 1.0a15 (2024-08-15) 1.0a14 (2024-08-05) 0.64.8 (2024-06-21) 0.64.7 (2024-06-12) 1.0a13 (2024-03-12) 1.0a12 (2024-02-29) 1.0a11 (2024-02-19) 1.0a10 (2024-02-17) 1.0a9 (2024-02-16) Alter table support for create, insert, upsert and update Permissions fix for the upsert API Permission checks now consider opinions from every plugin Other changes 1.0a8 (2024-02-07) Configuration JavaScript plugins Plugin hooks Documentation Minor fixes 0.64.6 (2023-12-22) 0.64.5 (2023-10-08) 1.0a7 (2023-09-21) 0.64.4 (2023-09-21) 1.0a6 (2023-09-07) 1.0a5 (2023-08-29) 1.0a4 (2023-08-21) 1.0a3 (2023-08-09) Smaller changes 0.64.2 (2023-03-08) 0.64.1 (2023-01-11) 0.64 (2023-01-09) 0.63.3 (2022-12-17) 1.0a2 (2022-12-14) 1.0a1 (2022-12-01) 1.0a0 (2022-11-29) Signed API tokens Write API 0.63.2 (2022-11-18) 0.63.1 (2022-11-10) 0.63 (2022-10-27) Features Plugin hooks and internals Documentation 0.62 (2022-08-14) Features Plugin hooks Bug fixes Documentation 0.61.1 (2022-03-23) 0.61 (2022-03-23) 0.60.2 (2022-02-07) 0.60.1 (2022-01-20) 0.60 (2022-01-13) Plugins and internals Faceting Other small fixes 0.59.4 (2021-11-29) 0.59.3 (2021-11-20) 0.59.2 (2021-11-13) 0.59.1 (2021-10-24) 0.59 (2021-10-14) 0.58.1 (2021-07-16) 0.58 (2021-07-14) 0.57.1 (2021-06-08) 0.57 (2021-06-05) New features Bug fixes and other improvements 0.56.1 (2021-06-05) 0.56 (2021-03-28) 0.55 (2021-02-18) 0.54.1 (2021-02-02) 0.54 (2021-01-25) The _internal database Named in-memory database support JavaScript modules Code formatting with Black and Prettier Other changes 0.53 (2020-12-10) 0.52.5 (2020-12-09) 0.52.4 (2020-12-05) 0.52.3 (2020-12-03) 0.52.2 (2020-12-02) 0.52.1 (2020-11-29) 0.52 (2020-11-28) 0.51.1 (2020-10-31) 0.51 (2020-10-31) New visual design Plugins can now add links within Datasette Binary data URL building Running Datasette behind a proxy Smaller changes 0.50.2 (2020-10-09) 0.50.1 (2020-10-09) 0.50 (2020-10-09) 0.49.1 (2020-09-15) 0.49 (2020-09-14) 0.48 (2020-08-16) 0.47.3 (2020-08-15) 0.47.2 (2020-08-12) 0.47.1 (2020-08-11) 0.47 (2020-08-11) 0.46 (2020-08-09) 0.45 (2020-07-01) Magic parameters for canned queries Log out Better plugin documentation New plugin hooks Smaller changes 0.44 (2020-06-11) Authentication Permissions Writable canned queries Flash messages Signed values and secrets CSRF protection Cookie methods register_routes() plugin hooks Smaller changes The road to Datasette 1.0 0.43 (2020-05-28) 0.42 (2020-05-08) 0.41 (2020-05-06) 0.40 (2020-04-21) 0.39 (2020-03-24) 0.38 (2020-03-08) 0.37.1 (2020-03-02) 0.37 (2020-02-25) 0.36 (2020-02-21) 0.35 (2020-02-04) 0.34 (2020-01-29) 0.33 (2019-12-22) 0.32 (2019-11-14) 0.31.2 (2019-11-13) 0.31.1 (2019-11-12) 0.31 (2019-11-11) 0.30.2 (2019-11-02) 0.30.1 (2019-10-30) 0.30 (2019-10-18) 0.29.3 (2019-09-02) 0.29.2 (2019-07-13) 0.29.1 (2019-07-11) 0.29 (2019-07-07) ASGI New plugin hook: asgi_wrapper New plugin hook: extra_template_vars Secret plugin configuration options Facet by date Easier custom templates for table rows ?_through= for joins through many-to-many tables Small changes 0.28 (2019-05-19) Supporting databases that change Faceting improvements, and faceting plugins datasette publish cloudrun register_output_renderer plugins Medium changes Small changes 0.27.1 (2019-05-09) 0.27 (2019-01-31) 0.26.1 (2019-01-10) 0.26 (2019-01-02) 0.25.2 (2018-12-16) 0.25.1 (2018-11-04) 0.25 (2018-09-19) 0.24 (2018-07-23) 0.23.2 (2018-07-07) 0.23.1 (2018-06-21) 0.23 (2018-06-18) CSV export Foreign key expansions New configuration settings Control HTTP caching with ?_ttl= Improved support for SpatiaLite latest.datasette.io Miscellaneous 0.22.1 (2018-05-23) 0.22 (2018-05-20) 0.21 (2018-05-05) 0.20 (2018-04-20) 0.19 (2018-04-16) 0.18 (2018-04-14) 0.17 (2018-04-13) 0.16 (2018-04-13) 0.15 (2018-04-09) 0.14 (2017-12-09) 0.13 (2017-11-24) 0.12 (2017-11-16) 0.11 (2017-11-14) 0.10 (2017-11-14) 0.9 (2017-11-13) 0.8 (2017-11-13)",422, 709,Datasette,"An open source multi-tool for exploring and publishing data Datasette is a tool for exploring and publishing data. It helps people take data of any shape or size and publish that as an interactive, explorable website and accompanying API. Datasette is aimed at data journalists, museum curators, archivists, local governments and anyone else who has data that they wish to share with the world. It is part of a wider ecosystem of tools and plugins dedicated to making working with structured data as productive as possible. Explore a demo , watch a presentation about the project . Interested in learning Datasette? Start with the official tutorials . Support questions, feedback? Join the Datasette Discord .",422, 708,debug-menu,Controls if the various debug pages are displayed in the jump menu.,422, 707,permissions-debug,Actor is allowed to view the /-/permissions debug tools.,422, 706,execute-write-sql,"Actor is allowed to run arbitrary writable SQL queries against a specific database using the write SQL queries page , subject to table-level write permissions such as insert-row , update-row and delete-row . SQL functions are allowed and are not separately restricted by Datasette permissions. resource - datasette.resources.DatabaseResource(database) database is the name of the database (string)",422, 705,execute-sql,"Actor is allowed to run arbitrary read-only SQL queries against a specific database using the custom SQL query page , 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 .",422, 704,drop-view,"Actor is allowed to drop a database view. resource - datasette.resources.TableResource(database, table) database is the name of the database (string) table is the name of the view (string)",422, 703,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)",422, 702,set-column-type,"Actor is allowed to set assigned column types for columns 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)",422, 701,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)",422, 700,create-view,"Actor is allowed to create a database view. resource - datasette.resources.DatabaseResource(database) database is the name of the database (string)",422, 699,create-table,"Actor is allowed to create a database table. resource - datasette.resources.DatabaseResource(database) database is the name of the database (string)",422, 698,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)",422, 697,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)",422, 696,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)",422, 695,delete-query,"Actor is allowed to delete a stored query. resource - datasette.resources.QueryResource(database, query) database is the name of the database (string) query is the name of the query (string)",422, 694,update-query,"Actor is allowed to update a stored query. resource - datasette.resources.QueryResource(database, query) database is the name of the database (string) query is the name of the query (string)",422, 693,store-query,"Actor is allowed to create stored queries against a database. resource - datasette.resources.DatabaseResource(database) database is the name of the database (string)",422,