{"id": "changelog:v1-0-a1", "page": "changelog", "ref": "v1-0-a1", "title": "1.0a1 (2022-12-01)", "content": "Write APIs now serve correct CORS headers if Datasette is started in --cors mode. See the full list of CORS headers in the documentation. ( #1922 ) \n \n \n Fixed a bug where the _memory database could be written to even though writes were not persisted. ( #1917 ) \n \n \n The https://latest.datasette.io/ demo instance now includes an ephemeral database which can be used to test Datasette's write APIs, using the new datasette-ephemeral-tables plugin to drop any created tables after five minutes. This database is only available if you sign in as the root user using the link on the homepage. ( #1915 ) \n \n \n Fixed a bug where hitting the write endpoints with a GET request returned a 500 error. It now returns a 405 (method not allowed) error instead. ( #1916 ) \n \n \n The list of endpoints in the API explorer now lists mutable databases first. ( #1918 ) \n \n \n The \"ignore\": true and \"replace\": true options for the insert API are now documented . ( #1924 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/1922\", \"label\": \"#1922\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1917\", \"label\": \"#1917\"}, {\"href\": \"https://latest.datasette.io/\", \"label\": \"https://latest.datasette.io/\"}, {\"href\": \"https://datasette.io/plugins/datasette-ephemeral-tables\", \"label\": \"datasette-ephemeral-tables\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1915\", \"label\": \"#1915\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1916\", \"label\": \"#1916\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1918\", \"label\": \"#1918\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1924\", \"label\": \"#1924\"}]"} {"id": "changelog:v1-0-a10", "page": "changelog", "ref": "v1-0-a10", "title": "1.0a10 (2024-02-17)", "content": "The only changes in this alpha correspond to the way Datasette handles database transactions. ( #2277 ) \n \n \n The database.execute_write_fn() method has a new transaction=True parameter. This defaults to True which means all functions executed using this method are now automatically wrapped in a transaction - previously the functions needed to roll transaction handling on their own, and many did not. \n \n \n Pass transaction=False to execute_write_fn() if you want to manually handle transactions in your function. \n \n \n Several internal Datasette features, including parts of the JSON write API , had been failing to wrap their operations in a transaction. This has been fixed by the new transaction=True default.", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/2277\", \"label\": \"#2277\"}]"} {"id": "changelog:v1-0-a11", "page": "changelog", "ref": "v1-0-a11", "title": "1.0a11 (2024-02-19)", "content": "The \"replace\": true argument to the /db/table/-/insert API now requires the actor to have the update-row permission. ( #2279 ) \n \n \n Fixed some UI bugs in the interactive permissions debugging tool. ( #2278 ) \n \n \n The column action menu now aligns better with the cog icon, and positions itself taking into account the width of the browser window. ( #2263 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/2279\", \"label\": \"#2279\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2278\", \"label\": \"#2278\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2263\", \"label\": \"#2263\"}]"} {"id": "changelog:v1-0-a12", "page": "changelog", "ref": "v1-0-a12", "title": "1.0a12 (2024-02-29)", "content": "New query_actions() plugin hook, similar to table_actions() and database_actions() . Can be used to add a menu of actions to the canned query or arbitrary SQL query page. ( #2283 ) \n \n \n New design for the button that opens the query, table and database actions menu. ( #2281 ) \n \n \n \"does not contain\" table filter for finding rows that do not contain a string. ( #2287 ) \n \n \n Fixed a bug in the makeColumnActions(columnDetails) JavaScript plugin mechanism where the column action menu was not fully reset in between each interaction. ( #2289 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/2283\", \"label\": \"#2283\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2281\", \"label\": \"#2281\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2287\", \"label\": \"#2287\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2289\", \"label\": \"#2289\"}]"} {"id": "changelog:v1-0-a13", "page": "changelog", "ref": "v1-0-a13", "title": "1.0a13 (2024-03-12)", "content": "Each of the key concepts in Datasette now has an actions menu , which plugins can use to add additional functionality targeting that entity. \n \n \n Plugin hook: view_actions() for actions that can be applied to a SQL view. ( #2297 ) \n \n \n Plugin hook: homepage_actions() for actions that apply to the instance homepage. ( #2298 ) \n \n \n Plugin hook: row_actions() for actions that apply to the row page. ( #2299 ) \n \n \n Action menu items for all of the *_actions() plugin hooks can now return an optional \"description\" key, which will be displayed in the menu below the action label. ( #2294 ) \n \n \n Plugin hooks documentation page is now organized with additional headings. ( #2300 ) \n \n \n Improved the display of action buttons on pages that also display metadata. ( #2286 ) \n \n \n The header and footer of the page now uses a subtle gradient effect, and options in the navigation menu are better visually defined. ( #2302 ) \n \n \n Table names that start with an underscore now default to hidden. ( #2104 ) \n \n \n pragma_table_list has been added to the allow-list of SQLite pragma functions supported by Datasette. select * from pragma_table_list() is no longer blocked. ( #2104 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/2297\", \"label\": \"#2297\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2298\", \"label\": \"#2298\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2299\", \"label\": \"#2299\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2294\", \"label\": \"#2294\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2300\", \"label\": \"#2300\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2286\", \"label\": \"#2286\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2302\", \"label\": \"#2302\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2104\", \"label\": \"#2104\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2104#issuecomment-1982352475\", \"label\": \"#2104\"}]"} {"id": "changelog:v1-0-a2", "page": "changelog", "ref": "v1-0-a2", "title": "1.0a2 (2022-12-14)", "content": "The third Datasette 1.0 alpha release adds upsert support to the JSON API, plus the ability to specify finely grained permissions when creating an API token. \n See Datasette 1.0a2: Upserts and finely grained permissions for an extended, annotated version of these release notes. \n \n \n New /db/table/-/upsert API, documented here . upsert is an update-or-insert: existing rows will have specified keys updated, but if no row matches the incoming primary key a brand new row will be inserted instead. ( #1878 ) \n \n \n New register_permissions(datasette) plugin hook. Plugins can now register named permissions, which will then be listed in various interfaces that show available permissions. ( #1940 ) \n \n \n The /db/-/create API for creating a table now accepts \"ignore\": true and \"replace\": true options when called with the \"rows\" property that creates a new table based on an example set of rows. This means the API can be called multiple times with different rows, setting rules for what should happen if a primary key collides with an existing row. ( #1927 ) \n \n \n Arbitrary permissions can now be configured at the instance, database and resource (table, SQL view or canned query) level in Datasette's Metadata JSON and YAML files. The new \"permissions\" key can be used to specify which actors should have which permissions. See Other permissions in datasette.yaml for details. ( #1636 ) \n \n \n The /-/create-token page can now be used to create API tokens which are restricted to just a subset of actions, including against specific databases or resources. See API Tokens for details. ( #1947 ) \n \n \n Likewise, the datasette create-token CLI command can now create tokens with a subset of permissions . ( #1855 ) \n \n \n New datasette.create_token() API method for programmatically creating signed API tokens. ( #1951 ) \n \n \n /db/-/create API now requires actor to have insert-row permission in order to use the \"row\" or \"rows\" properties. ( #1937 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://simonwillison.net/2022/Dec/15/datasette-1a2/\", \"label\": \"Datasette 1.0a2: Upserts and finely grained permissions\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1878\", \"label\": \"#1878\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1940\", \"label\": \"#1940\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1927\", \"label\": \"#1927\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1636\", \"label\": \"#1636\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1947\", \"label\": \"#1947\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1855\", \"label\": \"#1855\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1951\", \"label\": \"#1951\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1937\", \"label\": \"#1937\"}]"} {"id": "changelog:v1-0-a3", "page": "changelog", "ref": "v1-0-a3", "title": "1.0a3 (2023-08-09)", "content": "This alpha release previews the updated design for Datasette's default JSON API. ( #782 ) \n The new default JSON representation for both table pages ( /dbname/table.json ) and arbitrary SQL queries ( /dbname.json?sql=... ) is now shaped like this: \n {\n \"ok\": true,\n \"rows\": [\n {\n \"id\": 3,\n \"name\": \"Detroit\"\n },\n {\n \"id\": 2,\n \"name\": \"Los Angeles\"\n },\n {\n \"id\": 4,\n \"name\": \"Memnonia\"\n },\n {\n \"id\": 1,\n \"name\": \"San Francisco\"\n }\n ],\n \"truncated\": false\n} \n Tables will include an additional \"next\" key for pagination, which can be passed to ?_next= to fetch the next page of results. \n The various ?_shape= options continue to work as before - see Different shapes for details. \n A new ?_extra= mechanism is available for tables, but has not yet been stabilized or documented. Details on that are available in #262 .", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/782\", \"label\": \"#782\"}, {\"href\": \"https://github.com/simonw/datasette/issues/262\", \"label\": \"#262\"}]"} {"id": "changelog:v1-0-a4", "page": "changelog", "ref": "v1-0-a4", "title": "1.0a4 (2023-08-21)", "content": "This alpha fixes a security issue with the /-/api API explorer. On authenticated Datasette instances (instances protected using plugins such as datasette-auth-passwords ) the API explorer interface could reveal the names of databases and tables within the protected instance. The data stored in those tables was not revealed. \n For more information and workarounds, read the security advisory . The issue has been present in every previous alpha version of Datasette 1.0: versions 1.0a0, 1.0a1, 1.0a2 and 1.0a3. \n Also in this alpha: \n \n \n The new datasette plugins --requirements option outputs a list of currently installed plugins in Python requirements.txt format, useful for duplicating that installation elsewhere. ( #2133 ) \n \n \n Writable canned queries can now define a on_success_message_sql field in their configuration, containing a SQL query that should be executed upon successful completion of the write operation in order to generate a message to be shown to the user. ( #2138 ) \n \n \n The automatically generated border color for a database is now shown in more places around the application. ( #2119 ) \n \n \n Every instance of example shell script code in the documentation should now include a working copy button, free from additional syntax. ( #2140 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-auth-passwords\", \"label\": \"datasette-auth-passwords\"}, {\"href\": \"https://github.com/simonw/datasette/security/advisories/GHSA-7ch3-7pp7-7cpq\", \"label\": \"the security advisory\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2133\", \"label\": \"#2133\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2138\", \"label\": \"#2138\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2119\", \"label\": \"#2119\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2140\", \"label\": \"#2140\"}]"} {"id": "changelog:v1-0-a5", "page": "changelog", "ref": "v1-0-a5", "title": "1.0a5 (2023-08-29)", "content": "When restrictions are applied to API tokens , those restrictions now behave slightly differently: applying the view-table restriction will imply the ability to view-database for the database containing that table, and both view-table and view-database will imply view-instance . Previously you needed to create a token with restrictions that explicitly listed view-instance and view-database and view-table in order to view a table without getting a permission denied error. ( #2102 ) \n \n \n New datasette.yaml (or .json ) configuration file, which can be specified using datasette -c path-to-file . The goal here to consolidate settings, plugin configuration, permissions, canned queries, and other Datasette configuration into a single single file, separate from metadata.yaml . The legacy settings.json config file used for Configuration directory mode has been removed, and datasette.yaml has a \"settings\" section where the same settings key/value pairs can be included. In the next future alpha release, more configuration such as plugins/permissions/canned queries will be moved to the datasette.yaml file. See #2093 for more details. Thanks, Alex Garcia. \n \n \n The -s/--setting option can now take dotted paths to nested settings. These will then be used to set or over-ride the same options as are present in the new configuration file. ( #2156 ) \n \n \n New --actor '{\"id\": \"json-goes-here\"}' option for use with datasette --get to treat the simulated request as being made by a specific actor, see datasette --get . ( #2153 ) \n \n \n The Datasette _internal database has had some changes. It no longer shows up in the datasette.databases list by default, and is now instead available to plugins using the datasette.get_internal_database() . Plugins are invited to use this as a private database to store configuration and settings and secrets that should not be made visible through the default Datasette interface. Users can pass the new --internal internal.db option to persist that internal database to disk. Thanks, Alex Garcia. ( #2157 ).", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/2102\", \"label\": \"#2102\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2093\", \"label\": \"#2093\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2156\", \"label\": \"#2156\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2153\", \"label\": \"#2153\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2157\", \"label\": \"#2157\"}]"} {"id": "changelog:v1-0-a6", "page": "changelog", "ref": "v1-0-a6", "title": "1.0a6 (2023-09-07)", "content": "New plugin hook: actors_from_ids(datasette, actor_ids) and an internal method to accompany it, await .actors_from_ids(actor_ids) . This mechanism is intended to be used by plugins that may need to display the actor who was responsible for something managed by that plugin: they can now resolve the recorded IDs of actors into the full actor objects. ( #2181 ) \n \n \n DATASETTE_LOAD_PLUGINS environment variable for controlling which plugins are loaded by Datasette. ( #2164 ) \n \n \n Datasette now checks if the user has permission to view a table linked to by a foreign key before turning that foreign key into a clickable link. ( #2178 ) \n \n \n The execute-sql permission now implies that the actor can also view the database and instance. ( #2169 ) \n \n \n Documentation describing a pattern for building plugins that themselves define further hooks for other plugins. ( #1765 ) \n \n \n Datasette is now tested against the Python 3.12 preview. ( #2175 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/2181\", \"label\": \"#2181\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2164\", \"label\": \"#2164\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2178\", \"label\": \"#2178\"}, {\"href\": \"https://github.com/simonw/datasette/issues/2169\", \"label\": \"#2169\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1765\", \"label\": \"#1765\"}, {\"href\": \"https://github.com/simonw/datasette/pull/2175\", \"label\": \"#2175\"}]"} {"id": "changelog:v1-0-a7", "page": "changelog", "ref": "v1-0-a7", "title": "1.0a7 (2023-09-21)", "content": "Fix for a crashing bug caused by viewing the table page for a named in-memory database. ( #2189 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/2189\", \"label\": \"#2189\"}]"} {"id": "changelog:v1-0-a8", "page": "changelog", "ref": "v1-0-a8", "title": "1.0a8 (2024-02-07)", "content": "This alpha release continues the migration of Datasette's configuration from metadata.yaml to the new datasette.yaml configuration file, introduces a new system for JavaScript plugins and adds several new plugin hooks. \n See Datasette 1.0a8: JavaScript plugins, new plugin hooks and plugin configuration in datasette.yaml for an annotated version of these release notes.", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://simonwillison.net/2024/Feb/7/datasette-1a8/\", \"label\": \"Datasette 1.0a8: JavaScript plugins, new plugin hooks and plugin configuration in datasette.yaml\"}]"} {"id": "changelog:v1-0-a9", "page": "changelog", "ref": "v1-0-a9", "title": "1.0a9 (2024-02-16)", "content": "This alpha release adds basic alter table support to the Datasette Write API and fixes a permissions bug relating to the /upsert API endpoint.", "breadcrumbs": "[\"Changelog\"]", "references": "[]"} {"id": "changelog:writable-canned-queries", "page": "changelog", "ref": "writable-canned-queries", "title": "Writable canned queries", "content": "Datasette's Canned queries feature lets you define SQL queries in metadata.json which can then be executed by users visiting a specific URL. https://latest.datasette.io/fixtures/neighborhood_search for example. \n Canned queries were previously restricted to SELECT , but Datasette 0.44 introduces the ability for canned queries to execute INSERT or UPDATE queries as well, using the new \"write\": true property ( #800 ): \n {\n \"databases\": {\n \"dogs\": {\n \"queries\": {\n \"add_name\": {\n \"sql\": \"INSERT INTO names (name) VALUES (:name)\",\n \"write\": true\n }\n }\n }\n }\n} \n See Writable canned queries for more details.", "breadcrumbs": "[\"Changelog\", \"0.44 (2020-06-11)\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/neighborhood_search\", \"label\": \"https://latest.datasette.io/fixtures/neighborhood_search\"}, {\"href\": \"https://github.com/simonw/datasette/issues/800\", \"label\": \"#800\"}]"} {"id": "changelog:write-api", "page": "changelog", "ref": "write-api", "title": "Write API", "content": "New API explorer at /-/api for trying out the API. ( #1871 ) \n \n \n /db/-/create API for Creating a table . ( #1882 ) \n \n \n /db/table/-/insert API for Inserting rows . ( #1851 ) \n \n \n /db/table/-/drop API for Dropping tables . ( #1874 ) \n \n \n /db/table/pk/-/update API for Updating a row . ( #1863 ) \n \n \n /db/table/pk/-/delete API for Deleting a row . ( #1864 )", "breadcrumbs": "[\"Changelog\", \"1.0a0 (2022-11-29)\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/1871\", \"label\": \"#1871\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1882\", \"label\": \"#1882\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1851\", \"label\": \"#1851\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1874\", \"label\": \"#1874\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1863\", \"label\": \"#1863\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1864\", \"label\": \"#1864\"}]"} {"id": "cli-reference:cli-datasette-get", "page": "cli-reference", "ref": "cli-datasette-get", "title": "datasette --get", "content": "The --get option to datasette serve (or just datasette ) specifies the path to a page within Datasette and causes Datasette to output the content from that path without starting the web server. \n This means that all of Datasette's functionality can be accessed directly from the command-line. \n For example: \n datasette --get '/-/versions.json' | jq . \n {\n \"python\": {\n \"version\": \"3.8.5\",\n \"full\": \"3.8.5 (default, Jul 21 2020, 10:48:26) \\n[Clang 11.0.3 (clang-1103.0.32.62)]\"\n },\n \"datasette\": {\n \"version\": \"0.46+15.g222a84a.dirty\"\n },\n \"asgi\": \"3.0\",\n \"uvicorn\": \"0.11.8\",\n \"sqlite\": {\n \"version\": \"3.32.3\",\n \"fts_versions\": [\n \"FTS5\",\n \"FTS4\",\n \"FTS3\"\n ],\n \"extensions\": {\n \"json1\": null\n },\n \"compile_options\": [\n \"COMPILER=clang-11.0.3\",\n \"ENABLE_COLUMN_METADATA\",\n \"ENABLE_FTS3\",\n \"ENABLE_FTS3_PARENTHESIS\",\n \"ENABLE_FTS4\",\n \"ENABLE_FTS5\",\n \"ENABLE_GEOPOLY\",\n \"ENABLE_JSON1\",\n \"ENABLE_PREUPDATE_HOOK\",\n \"ENABLE_RTREE\",\n \"ENABLE_SESSION\",\n \"MAX_VARIABLE_NUMBER=250000\",\n \"THREADSAFE=1\"\n ]\n }\n} \n You can use the --token TOKEN option to send an API token with the simulated request. \n Or you can make a request as a specific actor by passing a JSON representation of that actor to --actor : \n datasette --memory --actor '{\"id\": \"root\"}' --get '/-/actor.json' \n The exit code of datasette --get will be 0 if the request succeeds and 1 if the request produced an HTTP status code other than 200 - e.g. a 404 or 500 error. \n This lets you use datasette --get / to run tests against a Datasette application in a continuous integration environment such as GitHub Actions.", "breadcrumbs": "[\"CLI reference\", \"datasette serve\"]", "references": "[]"} {"id": "cli-reference:cli-help-create-token-help", "page": "cli-reference", "ref": "cli-help-create-token-help", "title": "datasette create-token", "content": "Create a signed API token, see datasette create-token . \n [[[cog\nhelp([\"create-token\", \"--help\"]) \n ]]] \n Usage: datasette create-token [OPTIONS] ID\n\n Create a signed API token for the specified actor ID\n\n Example:\n\n datasette create-token root --secret mysecret\n\n To allow only \"view-database-download\" for all databases:\n\n datasette create-token root --secret mysecret \\\n --all view-database-download\n\n To allow \"create-table\" against a specific database:\n\n datasette create-token root --secret mysecret \\\n --database mydb create-table\n\n To allow \"insert-row\" against a specific table:\n\n datasette create-token root --secret myscret \\\n --resource mydb mytable insert-row\n\n Restricted actions can be specified multiple times using multiple --all,\n --database, and --resource options.\n\n Add --debug to see a decoded version of the token.\n\nOptions:\n --secret TEXT Secret used for signing the API tokens\n [required]\n -e, --expires-after INTEGER Token should expire after this many seconds\n -a, --all ACTION Restrict token to this action\n -d, --database DB ACTION Restrict token to this action on this database\n -r, --resource DB RESOURCE ACTION\n Restrict token to this action on this database\n resource (a table, SQL view or named query)\n --debug Show decoded token\n --plugins-dir DIRECTORY Path to directory containing custom plugins\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-help", "page": "cli-reference", "ref": "cli-help-help", "title": "datasette --help", "content": "Running datasette --help shows a list of all of the available commands. \n [[[cog\nhelp([\"--help\"]) \n ]]] \n Usage: datasette [OPTIONS] COMMAND [ARGS]...\n\n Datasette is an open source multi-tool for exploring and publishing data\n\n About Datasette: https://datasette.io/\n Full documentation: https://docs.datasette.io/\n\nOptions:\n --version Show the version and exit.\n --help Show this message and exit.\n\nCommands:\n serve* Serve up specified SQLite database files with a web UI\n create-token Create a signed API token for the specified actor ID\n inspect Generate JSON summary of provided database files\n install Install plugins and packages from PyPI into the same...\n package Package SQLite files into a Datasette Docker container\n plugins List currently installed plugins\n publish Publish specified SQLite database files to the internet...\n uninstall Uninstall plugins and Python packages from the Datasette... \n [[[end]]] \n Additional commands added by plugins that use the register_commands(cli) hook will be listed here as well.", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-inspect-help", "page": "cli-reference", "ref": "cli-help-inspect-help", "title": "datasette inspect", "content": "Outputs JSON representing introspected data about one or more SQLite database files. \n If you are opening an immutable database, you can pass this file to the --inspect-data option to improve Datasette's performance by allowing it to skip running row counts against the database when it first starts running: \n datasette inspect mydatabase.db > inspect-data.json\ndatasette serve -i mydatabase.db --inspect-file inspect-data.json \n This performance optimization is used automatically by some of the datasette publish commands. You are unlikely to need to apply this optimization manually. \n [[[cog\nhelp([\"inspect\", \"--help\"]) \n ]]] \n Usage: datasette inspect [OPTIONS] [FILES]...\n\n Generate JSON summary of provided database files\n\n This can then be passed to \"datasette --inspect-file\" to speed up count\n operations against immutable database files.\n\nOptions:\n --inspect-file TEXT\n --load-extension PATH:ENTRYPOINT?\n Path to a SQLite extension to load, and\n optional entrypoint\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-install-help", "page": "cli-reference", "ref": "cli-help-install-help", "title": "datasette install", "content": "Install new Datasette plugins. This command works like pip install but ensures that your plugins will be installed into the same environment as Datasette. \n This command: \n datasette install datasette-cluster-map \n Would install the datasette-cluster-map plugin. \n [[[cog\nhelp([\"install\", \"--help\"]) \n ]]] \n Usage: datasette install [OPTIONS] [PACKAGES]...\n\n Install plugins and packages from PyPI into the same environment as Datasette\n\nOptions:\n -U, --upgrade Upgrade packages to latest version\n -r, --requirement PATH Install from requirements file\n -e, --editable TEXT Install a project in editable mode from this path\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-cluster-map\", \"label\": \"datasette-cluster-map\"}]"} {"id": "cli-reference:cli-help-package-help", "page": "cli-reference", "ref": "cli-help-package-help", "title": "datasette package", "content": "Package SQLite files into a Datasette Docker container, see datasette package . \n [[[cog\nhelp([\"package\", \"--help\"]) \n ]]] \n Usage: datasette package [OPTIONS] FILES...\n\n Package SQLite files into a Datasette Docker container\n\nOptions:\n -t, --tag TEXT Name for the resulting Docker container, can\n optionally use name:tag format\n -m, --metadata FILENAME Path to JSON/YAML file containing metadata to\n publish\n --extra-options TEXT Extra options to pass to datasette serve\n --branch TEXT Install datasette from a GitHub branch e.g. main\n --template-dir DIRECTORY Path to directory containing custom templates\n --plugins-dir DIRECTORY Path to directory containing custom plugins\n --static MOUNT:DIRECTORY Serve static files from this directory at /MOUNT/...\n --install TEXT Additional packages (e.g. plugins) to install\n --spatialite Enable SpatialLite extension\n --version-note TEXT Additional note to show on /-/versions\n --secret TEXT Secret used for signing secure values, such as\n signed cookies\n -p, --port INTEGER RANGE Port to run the server on, defaults to 8001\n [1<=x<=65535]\n --title TEXT Title for metadata\n --license TEXT License label for metadata\n --license_url TEXT License URL for metadata\n --source TEXT Source label for metadata\n --source_url TEXT Source URL for metadata\n --about TEXT About label for metadata\n --about_url TEXT About URL for metadata\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-plugins-help", "page": "cli-reference", "ref": "cli-help-plugins-help", "title": "datasette plugins", "content": "Output JSON showing all currently installed plugins, their versions, whether they include static files or templates and which Plugin hooks they use. \n [[[cog\nhelp([\"plugins\", \"--help\"]) \n ]]] \n Usage: datasette plugins [OPTIONS]\n\n List currently installed plugins\n\nOptions:\n --all Include built-in default plugins\n --requirements Output requirements.txt of installed plugins\n --plugins-dir DIRECTORY Path to directory containing custom plugins\n --help Show this message and exit. \n [[[end]]] \n Example output: \n [\n {\n \"name\": \"datasette-geojson\",\n \"static\": false,\n \"templates\": false,\n \"version\": \"0.3.1\",\n \"hooks\": [\n \"register_output_renderer\"\n ]\n },\n {\n \"name\": \"datasette-geojson-map\",\n \"static\": true,\n \"templates\": false,\n \"version\": \"0.4.0\",\n \"hooks\": [\n \"extra_body_script\",\n \"extra_css_urls\",\n \"extra_js_urls\"\n ]\n },\n {\n \"name\": \"datasette-leaflet\",\n \"static\": true,\n \"templates\": false,\n \"version\": \"0.2.2\",\n \"hooks\": [\n \"extra_body_script\",\n \"extra_template_vars\"\n ]\n }\n]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-publish-cloudrun-help", "page": "cli-reference", "ref": "cli-help-publish-cloudrun-help", "title": "datasette publish cloudrun", "content": "See Publishing to Google Cloud Run . \n [[[cog\nhelp([\"publish\", \"cloudrun\", \"--help\"]) \n ]]] \n Usage: datasette publish cloudrun [OPTIONS] [FILES]...\n\n Publish databases to Datasette running on Cloud Run\n\nOptions:\n -m, --metadata FILENAME Path to JSON/YAML file containing metadata to\n publish\n --extra-options TEXT Extra options to pass to datasette serve\n --branch TEXT Install datasette from a GitHub branch e.g.\n main\n --template-dir DIRECTORY Path to directory containing custom templates\n --plugins-dir DIRECTORY Path to directory containing custom plugins\n --static MOUNT:DIRECTORY Serve static files from this directory at\n /MOUNT/...\n --install TEXT Additional packages (e.g. plugins) to install\n --plugin-secret ...\n Secrets to pass to plugins, e.g. --plugin-\n secret datasette-auth-github client_id xxx\n --version-note TEXT Additional note to show on /-/versions\n --secret TEXT Secret used for signing secure values, such as\n signed cookies\n --title TEXT Title for metadata\n --license TEXT License label for metadata\n --license_url TEXT License URL for metadata\n --source TEXT Source label for metadata\n --source_url TEXT Source URL for metadata\n --about TEXT About label for metadata\n --about_url TEXT About URL for metadata\n -n, --name TEXT Application name to use when building\n --service TEXT Cloud Run service to deploy (or over-write)\n --spatialite Enable SpatialLite extension\n --show-files Output the generated Dockerfile and\n metadata.json\n --memory TEXT Memory to allocate in Cloud Run, e.g. 1Gi\n --cpu [1|2|4] Number of vCPUs to allocate in Cloud Run\n --timeout INTEGER Build timeout in seconds\n --apt-get-install TEXT Additional packages to apt-get install\n --max-instances INTEGER Maximum Cloud Run instances\n --min-instances INTEGER Minimum Cloud Run instances\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-publish-help", "page": "cli-reference", "ref": "cli-help-publish-help", "title": "datasette publish", "content": "Shows a list of available deployment targets for publishing data with Datasette. \n Additional deployment targets can be added by plugins that use the publish_subcommand(publish) hook. \n [[[cog\nhelp([\"publish\", \"--help\"]) \n ]]] \n Usage: datasette publish [OPTIONS] COMMAND [ARGS]...\n\n Publish specified SQLite database files to the internet along with a\n Datasette-powered interface and API\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n cloudrun Publish databases to Datasette running on Cloud Run\n heroku Publish databases to Datasette running on Heroku \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-publish-heroku-help", "page": "cli-reference", "ref": "cli-help-publish-heroku-help", "title": "datasette publish heroku", "content": "See Publishing to Heroku . \n [[[cog\nhelp([\"publish\", \"heroku\", \"--help\"]) \n ]]] \n Usage: datasette publish heroku [OPTIONS] [FILES]...\n\n Publish databases to Datasette running on Heroku\n\nOptions:\n -m, --metadata FILENAME Path to JSON/YAML file containing metadata to\n publish\n --extra-options TEXT Extra options to pass to datasette serve\n --branch TEXT Install datasette from a GitHub branch e.g.\n main\n --template-dir DIRECTORY Path to directory containing custom templates\n --plugins-dir DIRECTORY Path to directory containing custom plugins\n --static MOUNT:DIRECTORY Serve static files from this directory at\n /MOUNT/...\n --install TEXT Additional packages (e.g. plugins) to install\n --plugin-secret ...\n Secrets to pass to plugins, e.g. --plugin-\n secret datasette-auth-github client_id xxx\n --version-note TEXT Additional note to show on /-/versions\n --secret TEXT Secret used for signing secure values, such as\n signed cookies\n --title TEXT Title for metadata\n --license TEXT License label for metadata\n --license_url TEXT License URL for metadata\n --source TEXT Source label for metadata\n --source_url TEXT Source URL for metadata\n --about TEXT About label for metadata\n --about_url TEXT About URL for metadata\n -n, --name TEXT Application name to use when deploying\n --tar TEXT --tar option to pass to Heroku, e.g.\n --tar=/usr/local/bin/gtar\n --generate-dir DIRECTORY Output generated application files and stop\n without deploying\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-serve-help", "page": "cli-reference", "ref": "cli-help-serve-help", "title": "datasette serve", "content": "This command starts the Datasette web application running on your machine: \n datasette serve mydatabase.db \n Or since this is the default command you can run this instead: \n datasette mydatabase.db \n Once started you can access it at http://localhost:8001 \n [[[cog\nhelp([\"serve\", \"--help\"]) \n ]]] \n Usage: datasette serve [OPTIONS] [FILES]...\n\n Serve up specified SQLite database files with a web UI\n\nOptions:\n -i, --immutable PATH Database files to open in immutable mode\n -h, --host TEXT Host for server. Defaults to 127.0.0.1 which\n means only connections from the local machine\n will be allowed. Use 0.0.0.0 to listen to all\n IPs and allow access from other machines.\n -p, --port INTEGER RANGE Port for server, defaults to 8001. Use -p 0 to\n automatically assign an available port.\n [0<=x<=65535]\n --uds TEXT Bind to a Unix domain socket\n --reload Automatically reload if code or metadata\n change detected - useful for development\n --cors Enable CORS by serving Access-Control-Allow-\n Origin: *\n --load-extension PATH:ENTRYPOINT?\n Path to a SQLite extension to load, and\n optional entrypoint\n --inspect-file TEXT Path to JSON file created using \"datasette\n inspect\"\n -m, --metadata FILENAME Path to JSON/YAML file containing\n license/source metadata\n --template-dir DIRECTORY Path to directory containing custom templates\n --plugins-dir DIRECTORY Path to directory containing custom plugins\n --static MOUNT:DIRECTORY Serve static files from this directory at\n /MOUNT/...\n --memory Make /_memory database available\n -c, --config FILENAME Path to JSON/YAML Datasette configuration file\n -s, --setting SETTING... nested.key, value setting to use in Datasette\n configuration\n --secret TEXT Secret used for signing secure values, such as\n signed cookies\n --root Output URL that sets a cookie authenticating\n the root user\n --get TEXT Run an HTTP GET request against this path,\n print results and exit\n --token TEXT API token to send with --get requests\n --actor TEXT Actor to use for --get requests (JSON string)\n --version-note TEXT Additional note to show on /-/versions\n --help-settings Show available settings\n --pdb Launch debugger on any errors\n -o, --open Open Datasette in your web browser\n --create Create database files if they do not exist\n --crossdb Enable cross-database joins using the /_memory\n database\n --nolock Ignore locking, open locked files in read-only\n mode\n --ssl-keyfile TEXT SSL key file\n --ssl-certfile TEXT SSL certificate file\n --internal PATH Path to a persistent Datasette internal SQLite\n database\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:cli-help-serve-help-settings", "page": "cli-reference", "ref": "cli-help-serve-help-settings", "title": "datasette serve --help-settings", "content": "This command outputs all of the available Datasette settings . \n These can be passed to datasette serve using datasette serve --setting name value . \n [[[cog\nhelp([\"--help-settings\"]) \n ]]] \n Settings:\n default_page_size Default page size for the table view\n (default=100)\n max_returned_rows Maximum rows that can be returned from a table or\n custom query (default=1000)\n max_insert_rows Maximum rows that can be inserted at a time using\n the bulk insert API (default=100)\n num_sql_threads Number of threads in the thread pool for\n executing SQLite queries (default=3)\n sql_time_limit_ms Time limit for a SQL query in milliseconds\n (default=1000)\n default_facet_size Number of values to return for requested facets\n (default=30)\n facet_time_limit_ms Time limit for calculating a requested facet\n (default=200)\n facet_suggest_time_limit_ms Time limit for calculating a suggested facet\n (default=50)\n allow_facet Allow users to specify columns to facet using\n ?_facet= parameter (default=True)\n allow_download Allow users to download the original SQLite\n database files (default=True)\n allow_signed_tokens Allow users to create and use signed API tokens\n (default=True)\n default_allow_sql Allow anyone to run arbitrary SQL queries\n (default=True)\n max_signed_tokens_ttl Maximum allowed expiry time for signed API tokens\n (default=0)\n suggest_facets Calculate and display suggested facets\n (default=True)\n default_cache_ttl Default HTTP cache TTL (used in Cache-Control:\n max-age= header) (default=5)\n cache_size_kb SQLite cache size in KB (0 == use SQLite default)\n (default=0)\n allow_csv_stream Allow .csv?_stream=1 to download all rows\n (ignoring max_returned_rows) (default=True)\n max_csv_mb Maximum size allowed for CSV export in MB - set 0\n to disable this limit (default=100)\n truncate_cells_html Truncate cells longer than this in HTML table\n view - set 0 to disable (default=2048)\n force_https_urls Force URLs in API output to always use https://\n protocol (default=False)\n template_debug Allow display of template debug information with\n ?_context=1 (default=False)\n trace_debug Allow display of SQL trace debug information with\n ?_trace=1 (default=False)\n base_url Datasette URLs should use this base path\n (default=/) \n [[[end]]]", "breadcrumbs": "[\"CLI reference\", \"datasette serve\"]", "references": "[]"} {"id": "cli-reference:cli-help-uninstall-help", "page": "cli-reference", "ref": "cli-help-uninstall-help", "title": "datasette uninstall", "content": "Uninstall one or more plugins. \n [[[cog\nhelp([\"uninstall\", \"--help\"]) \n ]]] \n Usage: datasette uninstall [OPTIONS] PACKAGES...\n\n Uninstall plugins and Python packages from the Datasette environment\n\nOptions:\n -y, --yes Don't ask for confirmation\n --help Show this message and exit. \n [[[end]]]", "breadcrumbs": "[\"CLI reference\"]", "references": "[]"} {"id": "cli-reference:id1", "page": "cli-reference", "ref": "id1", "title": "CLI reference", "content": "The datasette CLI tool provides a number of commands. \n Running datasette without specifying a command runs the default command, datasette serve . See datasette serve for the full list of options for that command. \n [[[cog\nfrom datasette import cli\nfrom click.testing import CliRunner\nimport textwrap\ndef help(args):\n title = \"datasette \" + \" \".join(args)\n cog.out(\"\\n::\\n\\n\")\n result = CliRunner().invoke(cli.cli, args)\n output = result.output.replace(\"Usage: cli \", \"Usage: datasette \")\n cog.out(textwrap.indent(output, ' '))\n cog.out(\"\\n\\n\") \n ]]] \n [[[end]]]", "breadcrumbs": "[]", "references": "[]"} {"id": "configuration:configuration-cli", "page": "configuration", "ref": "configuration-cli", "title": "Configuration via the command-line", "content": "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: \n datasette mydatabase.db \\\n --setting settings.default_page_size 50 \\\n --setting settings.sql_time_limit_ms 3500 \n 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. \n It also works for plugin configuration, for example for datasette-cluster-map : \n datasette mydatabase.db \\\n --setting plugins.datasette-cluster-map.latitude_column xlat \\\n --setting plugins.datasette-cluster-map.longitude_column xlon \n 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 : \n datasette mydatabase.db \\\n -s plugins.datasette-proxy-url.paths '[{\"path\": \"/proxy\", \"backend\": \"http://example.com/\"}]' \n This is equivalent to a datasette.yaml file containing the following: \n [[[cog\nfrom metadata_doc import config_example\nimport textwrap\nconfig_example(cog, textwrap.dedent(\n \"\"\"\n plugins:\n datasette-proxy-url:\n paths:\n - path: /proxy\n backend: http://example.com/\n \"\"\").strip()\n ) \n ]]] \n [[[end]]]", "breadcrumbs": "[\"Configuration\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-cluster-map\", \"label\": \"datasette-cluster-map\"}, {\"href\": \"https://datasette.io/plugins/datasette-proxy-url\", \"label\": \"datasette-proxy-url\"}]"} {"id": "configuration:configuration-reference", "page": "configuration", "ref": "configuration-reference", "title": null, "content": "The following example shows some of the valid configuration options that can exist inside datasette.yaml . \n [[[cog\nfrom metadata_doc import config_example\nimport textwrap\nconfig_example(cog, textwrap.dedent(\n \"\"\"\n # Datasette settings block\n settings:\n default_page_size: 50\n sql_time_limit_ms: 3500\n max_returned_rows: 2000\n\n # top-level plugin configuration\n plugins:\n datasette-my-plugin:\n key: valueA\n\n # Database and table-level configuration\n databases:\n your_db_name:\n # plugin configuration for the your_db_name database\n plugins:\n datasette-my-plugin:\n key: valueA\n tables:\n your_table_name:\n allow:\n # Only the root user can access this table\n id: root\n # plugin configuration for the your_table_name table\n # inside your_db_name database\n plugins:\n datasette-my-plugin:\n key: valueB\n \"\"\")\n ) \n ]]] \n [[[end]]]", "breadcrumbs": "[\"Configuration\"]", "references": "[]"} {"id": "configuration:configuration-reference-canned-queries", "page": "configuration", "ref": "configuration-reference-canned-queries", "title": "Canned queries configuration", "content": "Canned 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: \n [[[cog\nfrom metadata_doc import config_example, config_example\nconfig_example(cog, {\n \"databases\": {\n \"sf-trees\": {\n \"queries\": {\n \"just_species\": {\n \"sql\": \"select qSpecies from Street_Tree_List\"\n }\n }\n }\n }\n}) \n ]]] \n [[[end]]] \n See the canned queries documentation for more, including how to configure writable canned queries .", "breadcrumbs": "[\"Configuration\", null]", "references": "[]"} {"id": "configuration:configuration-reference-css-js", "page": "configuration", "ref": "configuration-reference-css-js", "title": "Custom CSS and JavaScript", "content": "Datasette can load additional CSS and JavaScript files, configured in datasette.yaml like this: \n [[[cog\nfrom metadata_doc import config_example\nconfig_example(cog, \"\"\"\n extra_css_urls:\n - https://simonwillison.net/static/css/all.bf8cd891642c.css\n extra_js_urls:\n - https://code.jquery.com/jquery-3.2.1.slim.min.js\n\"\"\") \n ]]] \n [[[end]]] \n The extra CSS and JavaScript files will be linked in the of every page: \n \n \n You can also specify a SRI (subresource integrity hash) for these assets: \n [[[cog\nconfig_example(cog, \"\"\"\n extra_css_urls:\n - url: https://simonwillison.net/static/css/all.bf8cd891642c.css\n sri: sha384-9qIZekWUyjCyDIf2YK1FRoKiPJq4PHt6tp/ulnuuyRBvazd0hG7pWbE99zvwSznI\n extra_js_urls:\n - url: https://code.jquery.com/jquery-3.2.1.slim.min.js\n sri: sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g=\n\"\"\") \n ]]] \n [[[end]]] \n This will produce: \n \n \n Modern browsers will only execute the stylesheet or JavaScript if the SRI hash\n matches the content served. You can generate hashes using www.srihash.org \n Items in \"extra_js_urls\" can specify \"module\": true if they reference JavaScript that uses JavaScript modules . This configuration: \n [[[cog\nconfig_example(cog, \"\"\"\n extra_js_urls:\n - url: https://example.datasette.io/module.js\n module: true\n\"\"\") \n ]]] \n [[[end]]] \n Will produce this HTML: \n ", "breadcrumbs": "[\"Configuration\", null]", "references": "[{\"href\": \"https://www.srihash.org/\", \"label\": \"www.srihash.org\"}, {\"href\": \"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\", \"label\": \"JavaScript modules\"}]"} {"id": "configuration:configuration-reference-permissions", "page": "configuration", "ref": "configuration-reference-permissions", "title": "Permissions configuration", "content": "Datasette's authentication and permissions system can also be configured using datasette.yaml . \n Here is a simple example: \n [[[cog\nfrom metadata_doc import config_example\nimport textwrap\nconfig_example(cog, textwrap.dedent(\n \"\"\"\n # Instance is only available to users 'sharon' and 'percy':\n allow:\n id:\n - sharon\n - percy\n\n # Only 'percy' is allowed access to the accounting database:\n databases:\n accounting:\n allow:\n id: percy\n \"\"\").strip()\n ) \n ]]] \n [[[end]]] \n Access permissions in datasette.yaml has the full details.", "breadcrumbs": "[\"Configuration\", null]", "references": "[]"} {"id": "configuration:configuration-reference-plugins", "page": "configuration", "ref": "configuration-reference-plugins", "title": "Plugin configuration", "content": "Datasette plugins often require configuration. This plugin configuration should be placed in plugins keys inside datasette.yaml . \n Most plugins are configured at the top-level of the file, using the plugins key: \n [[[cog\nfrom metadata_doc import config_example\nimport textwrap\nconfig_example(cog, textwrap.dedent(\n \"\"\"\n # inside datasette.yaml\n plugins:\n datasette-my-plugin:\n key: my_value\n \"\"\").strip()\n ) \n ]]] \n [[[end]]] \n 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: \n [[[cog\nfrom metadata_doc import config_example\nimport textwrap\nconfig_example(cog, textwrap.dedent(\n \"\"\"\n # inside datasette.yaml\n databases:\n my_database:\n # plugin configuration for the my_database database\n plugins:\n datasette-my-plugin:\n key: my_value\n my_other_database:\n tables:\n my_table:\n # plugin configuration for the my_table table inside the my_other_database database\n plugins:\n datasette-my-plugin:\n key: my_value\n \"\"\").strip()\n ) \n ]]] \n [[[end]]]", "breadcrumbs": "[\"Configuration\", null]", "references": "[]"} {"id": "configuration:configuration-reference-settings", "page": "configuration", "ref": "configuration-reference-settings", "title": "Settings", "content": "Settings can be configured in datasette.yaml with the settings key: \n [[[cog\nfrom metadata_doc import config_example\nimport textwrap\nconfig_example(cog, textwrap.dedent(\n \"\"\"\n # inside datasette.yaml\n settings:\n default_allow_sql: off\n default_page_size: 50\n \"\"\").strip()\n ) \n ]]] \n [[[end]]] \n 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.`", "breadcrumbs": "[\"Configuration\", null]", "references": "[]"} {"id": "configuration:id1", "page": "configuration", "ref": "id1", "title": "Configuration", "content": "Datasette offers several ways to configure your Datasette instances: server settings, plugin configuration, authentication, and more. \n Most configuration can be handled using a datasette.yaml configuration file, passed to datasette using the -c/--config flag: \n datasette mydatabase.db --config datasette.yaml \n This file can also use JSON, as datasette.json . YAML is recommended over JSON due to its support for comments and multi-line strings.", "breadcrumbs": "[]", "references": "[]"} {"id": "contributing:contributing-alpha-beta", "page": "contributing", "ref": "contributing-alpha-beta", "title": "Alpha and beta releases", "content": "Alpha and beta releases are published to preview upcoming features that may not yet be stable - in particular to preview new plugin hooks. \n You are welcome to try these out, but please be aware that details may change before the final release. \n Please join discussions on the issue tracker to share your thoughts and experiences with on alpha and beta features that you try out.", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues\", \"label\": \"discussions on the issue tracker\"}]"} {"id": "contributing:contributing-bug-fix-branch", "page": "contributing", "ref": "contributing-bug-fix-branch", "title": "Releasing bug fixes from a branch", "content": "If it's necessary to publish a bug fix release without shipping new features that have landed on main a release branch can be used. \n Create it from the relevant last tagged release like so: \n git branch 0.52.x 0.52.4\ngit checkout 0.52.x \n Next cherry-pick the commits containing the bug fixes: \n git cherry-pick COMMIT \n Write the release notes in the branch, and update the version number in version.py . Then push the branch: \n git push -u origin 0.52.x \n Once the tests have completed, publish the release from that branch target using the GitHub Draft a new release form. \n Finally, cherry-pick the commit with the release notes and version number bump across to main : \n git checkout main\ngit cherry-pick COMMIT\ngit push", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/releases/new\", \"label\": \"Draft a new release\"}]"} {"id": "contributing:contributing-continuous-deployment", "page": "contributing", "ref": "contributing-continuous-deployment", "title": "Continuously deployed demo instances", "content": "The demo instance at latest.datasette.io is re-deployed automatically to Google Cloud Run for every push to main that passes the test suite. This is implemented by the GitHub Actions workflow at .github/workflows/deploy-latest.yml . \n Specific branches can also be set to automatically deploy by adding them to the on: push: branches block at the top of the workflow YAML file. Branches configured in this way will be deployed to a new Cloud Run service whether or not their tests pass. \n The Cloud Run URL for a branch demo can be found in the GitHub Actions logs.", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://latest.datasette.io/\", \"label\": \"latest.datasette.io\"}, {\"href\": \"https://github.com/simonw/datasette/blob/main/.github/workflows/deploy-latest.yml\", \"label\": \".github/workflows/deploy-latest.yml\"}]"} {"id": "contributing:contributing-debugging", "page": "contributing", "ref": "contributing-debugging", "title": "Debugging", "content": "Any errors that occur while Datasette is running while display a stack trace on the console. \n You can tell Datasette to open an interactive pdb debugger session if an error occurs using the --pdb option: \n datasette --pdb fixtures.db", "breadcrumbs": "[\"Contributing\"]", "references": "[]"} {"id": "contributing:contributing-documentation", "page": "contributing", "ref": "contributing-documentation", "title": "Editing and building the documentation", "content": "Datasette's documentation lives in the docs/ directory and is deployed automatically using Read The Docs . \n The documentation is written using reStructuredText. You may find this article on The subset of reStructuredText worth committing to memory useful. \n You can build it locally by installing sphinx and sphinx_rtd_theme in your Datasette development environment and then running make html directly in the docs/ directory: \n # You may first need to activate your virtual environment:\nsource venv/bin/activate\n\n# Install the dependencies needed to build the docs\npip install -e .[docs]\n\n# Now build the docs\ncd docs/\nmake html \n This will create the HTML version of the documentation in docs/_build/html . You can open it in your browser like so: \n open _build/html/index.html \n Any time you make changes to a .rst file you can re-run make html to update the built documents, then refresh them in your browser. \n For added productivity, you can use use sphinx-autobuild to run Sphinx in auto-build mode. This will run a local webserver serving the docs that automatically rebuilds them and refreshes the page any time you hit save in your editor. \n sphinx-autobuild will have been installed when you ran pip install -e .[docs] . In your docs/ directory you can start the server by running the following: \n make livehtml \n Now browse to http://localhost:8000/ to view the documentation. Any edits you make should be instantly reflected in your browser.", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://readthedocs.org/\", \"label\": \"Read The Docs\"}, {\"href\": \"https://simonwillison.net/2018/Aug/25/restructuredtext/\", \"label\": \"The subset of reStructuredText worth committing to memory\"}, {\"href\": \"https://pypi.org/project/sphinx-autobuild/\", \"label\": \"sphinx-autobuild\"}]"} {"id": "contributing:contributing-documentation-cog", "page": "contributing", "ref": "contributing-documentation-cog", "title": "Running Cog", "content": "Some pages of documentation (in particular the CLI reference ) are automatically updated using Cog . \n To update these pages, run the following command: \n cog -r docs/*.rst", "breadcrumbs": "[\"Contributing\", \"Editing and building the documentation\"]", "references": "[{\"href\": \"https://github.com/nedbat/cog\", \"label\": \"Cog\"}]"} {"id": "contributing:contributing-formatting", "page": "contributing", "ref": "contributing-formatting", "title": "Code formatting", "content": "Datasette uses opinionated code formatters: Black for Python and Prettier for JavaScript. \n These formatters are enforced by Datasette's continuous integration: if a commit includes Python or JavaScript code that does not match the style enforced by those tools, the tests will fail. \n When developing locally, you can verify and correct the formatting of your code using these tools.", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://github.com/psf/black\", \"label\": \"Black\"}, {\"href\": \"https://prettier.io/\", \"label\": \"Prettier\"}]"} {"id": "contributing:contributing-formatting-black", "page": "contributing", "ref": "contributing-formatting-black", "title": "Running Black", "content": "Black will be installed when you run pip install -e '.[test]' . To test that your code complies with Black, run the following in your root datasette repository checkout: \n black . --check \n All done! \u2728 \ud83c\udf70 \u2728\n95 files would be left unchanged. \n If any of your code does not conform to Black you can run this to automatically fix those problems: \n black . \n reformatted ../datasette/setup.py\nAll done! \u2728 \ud83c\udf70 \u2728\n1 file reformatted, 94 files left unchanged.", "breadcrumbs": "[\"Contributing\", \"Code formatting\"]", "references": "[]"} {"id": "contributing:contributing-formatting-blacken-docs", "page": "contributing", "ref": "contributing-formatting-blacken-docs", "title": "blacken-docs", "content": "The blacken-docs command applies Black formatting rules to code examples in the documentation. Run it like this: \n blacken-docs -l 60 docs/*.rst", "breadcrumbs": "[\"Contributing\", \"Code formatting\"]", "references": "[{\"href\": \"https://pypi.org/project/blacken-docs/\", \"label\": \"blacken-docs\"}]"} {"id": "contributing:contributing-formatting-prettier", "page": "contributing", "ref": "contributing-formatting-prettier", "title": "Prettier", "content": "To install Prettier, install Node.js and then run the following in the root of your datasette repository checkout: \n npm install \n This will install Prettier in a node_modules directory. You can then check that your code matches the coding style like so: \n npm run prettier -- --check \n > prettier\n> prettier 'datasette/static/*[!.min].js' \"--check\"\n\nChecking formatting...\n[warn] datasette/static/plugins.js\n[warn] Code style issues found in the above file(s). Forgot to run Prettier? \n You can fix any problems by running: \n npm run fix", "breadcrumbs": "[\"Contributing\", \"Code formatting\"]", "references": "[{\"href\": \"https://nodejs.org/en/download/package-manager/\", \"label\": \"install Node.js\"}]"} {"id": "contributing:contributing-release", "page": "contributing", "ref": "contributing-release", "title": "Release process", "content": "Datasette releases are performed using tags. When a new release is published on GitHub, a GitHub Action workflow will perform the following: \n \n \n Run the unit tests against all supported Python versions. If the tests pass... \n \n \n Build a Docker image of the release and push a tag to https://hub.docker.com/r/datasetteproject/datasette \n \n \n Re-point the \"latest\" tag on Docker Hub to the new image \n \n \n Build a wheel bundle of the underlying Python source code \n \n \n Push that new wheel up to PyPI: https://pypi.org/project/datasette/ \n \n \n If the release is an alpha, navigate to https://readthedocs.org/projects/datasette/versions/ and search for the tag name in the \"Activate a version\" filter, then mark that version as \"active\" to ensure it will appear on the public ReadTheDocs documentation site. \n \n \n To deploy new releases you will need to have push access to the main Datasette GitHub repository. \n Datasette follows Semantic Versioning : \n major.minor.patch \n We increment major for backwards-incompatible releases. Datasette is currently pre-1.0 so the major version is always 0 . \n We increment minor for new features. \n We increment patch for bugfix releass. \n Alpha and beta releases may have an additional a0 or b0 prefix - the integer component will be incremented with each subsequent alpha or beta. \n To release a new version, first create a commit that updates the version number in datasette/version.py and the the changelog with highlights of the new version. An example commit can be seen here : \n # Update changelog\ngit commit -m \" Release 0.51a1\n\nRefs #1056, #1039, #998, #1045, #1033, #1036, #1034, #976, #1057, #1058, #1053, #1064, #1066\" -a\ngit push \n Referencing the issues that are part of the release in the commit message ensures the name of the release shows up on those issue pages, e.g. here . \n You can generate the list of issue references for a specific release by copying and pasting text from the release notes or GitHub changes-since-last-release view into this Extract issue numbers from pasted text tool. \n To create the tag for the release, create a new release on GitHub matching the new version number. You can convert the release notes to Markdown by copying and pasting the rendered HTML into this Paste to Markdown tool . \n Finally, post a news item about the release on datasette.io by editing the news.yaml file in that site's repository.", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/blob/main/.github/workflows/deploy-latest.yml\", \"label\": \"GitHub Action workflow\"}, {\"href\": \"https://hub.docker.com/r/datasetteproject/datasette\", \"label\": \"https://hub.docker.com/r/datasetteproject/datasette\"}, {\"href\": \"https://pypi.org/project/datasette/\", \"label\": \"https://pypi.org/project/datasette/\"}, {\"href\": \"https://readthedocs.org/projects/datasette/versions/\", \"label\": \"https://readthedocs.org/projects/datasette/versions/\"}, {\"href\": \"https://semver.org/\", \"label\": \"Semantic Versioning\"}, {\"href\": \"https://github.com/simonw/datasette/commit/0e1e89c6ba3d0fbdb0823272952cf356f3016def\", \"label\": \"commit can be seen here\"}, {\"href\": \"https://github.com/simonw/datasette/issues/581#ref-commit-d56f402\", \"label\": \"here\"}, {\"href\": \"https://observablehq.com/@simonw/extract-issue-numbers-from-pasted-text\", \"label\": \"Extract issue numbers from pasted text\"}, {\"href\": \"https://github.com/simonw/datasette/releases/new\", \"label\": \"a new release\"}, {\"href\": \"https://euangoddard.github.io/clipboard2markdown/\", \"label\": \"Paste to Markdown tool\"}, {\"href\": \"https://datasette.io/\", \"label\": \"datasette.io\"}, {\"href\": \"https://github.com/simonw/datasette.io/blob/main/news.yaml\", \"label\": \"news.yaml\"}]"} {"id": "contributing:contributing-running-tests", "page": "contributing", "ref": "contributing-running-tests", "title": "Running the tests", "content": "Once you have done this, you can run the Datasette unit tests from inside your datasette/ directory using pytest like so: \n pytest \n You can run the tests faster using multiple CPU cores with pytest-xdist like this: \n pytest -n auto -m \"not serial\" \n -n auto detects the number of available cores automatically. The -m \"not serial\" skips tests that don't work well in a parallel test environment. You can run those tests separately like so: \n pytest -m \"serial\"", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://docs.pytest.org/\", \"label\": \"pytest\"}, {\"href\": \"https://pypi.org/project/pytest-xdist/\", \"label\": \"pytest-xdist\"}]"} {"id": "contributing:contributing-upgrading-codemirror", "page": "contributing", "ref": "contributing-upgrading-codemirror", "title": "Upgrading CodeMirror", "content": "Datasette bundles CodeMirror for the SQL editing interface, e.g. on this page . Here are the steps for upgrading to a new version of CodeMirror: \n \n \n Install the packages with: \n npm i codemirror @codemirror/lang-sql \n \n \n Build the bundle using the version number from package.json with: \n node_modules/.bin/rollup datasette/static/cm-editor-6.0.1.js \\\n -f iife \\\n -n cm \\\n -o datasette/static/cm-editor-6.0.1.bundle.js \\\n -p @rollup/plugin-node-resolve \\\n -p @rollup/plugin-terser \n \n \n Update the version reference in the codemirror.html template.", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://codemirror.net/\", \"label\": \"CodeMirror\"}, {\"href\": \"https://latest.datasette.io/fixtures\", \"label\": \"this page\"}]"} {"id": "contributing:contributing-using-fixtures", "page": "contributing", "ref": "contributing-using-fixtures", "title": "Using fixtures", "content": "To run Datasette itself, type datasette . \n You're going to need at least one SQLite database. A quick way to get started is to use the fixtures database that Datasette uses for its own tests. \n You can create a copy of that database by running this command: \n python tests/fixtures.py fixtures.db \n Now you can run Datasette against the new fixtures database like so: \n datasette fixtures.db \n This will start a server at http://127.0.0.1:8001/ . \n Any changes you make in the datasette/templates or datasette/static folder will be picked up immediately (though you may need to do a force-refresh in your browser to see changes to CSS or JavaScript). \n If you want to change Datasette's Python code you can use the --reload option to cause Datasette to automatically reload any time the underlying code changes: \n datasette --reload fixtures.db \n You can also use the fixtures.py script to recreate the testing version of metadata.json used by the unit tests. To do that: \n python tests/fixtures.py fixtures.db fixtures-metadata.json \n Or to output the plugins used by the tests, run this: \n python tests/fixtures.py fixtures.db fixtures-metadata.json fixtures-plugins\nTest tables written to fixtures.db\n- metadata written to fixtures-metadata.json\nWrote plugin: fixtures-plugins/register_output_renderer.py\nWrote plugin: fixtures-plugins/view_name.py\nWrote plugin: fixtures-plugins/my_plugin.py\nWrote plugin: fixtures-plugins/messages_output_renderer.py\nWrote plugin: fixtures-plugins/my_plugin_2.py \n Then run Datasette like this: \n datasette fixtures.db -m fixtures-metadata.json --plugins-dir=fixtures-plugins/", "breadcrumbs": "[\"Contributing\"]", "references": "[]"} {"id": "contributing:devenvironment", "page": "contributing", "ref": "devenvironment", "title": "Setting up a development environment", "content": "If you have Python 3.8 or higher installed on your computer (on OS X the quickest way to do this is using homebrew ) you can install an editable copy of Datasette using the following steps. \n If you want to use GitHub to publish your changes, first create a fork of datasette under your own GitHub account. \n Now clone that repository somewhere on your computer: \n git clone git@github.com:YOURNAME/datasette \n If you want to get started without creating your own fork, you can do this instead: \n git clone git@github.com:simonw/datasette \n The next step is to create a virtual environment for your project and use it to install Datasette's dependencies: \n cd datasette\n# Create a virtual environment in ./venv\npython3 -m venv ./venv\n# Now activate the virtual environment, so pip can install into it\nsource venv/bin/activate\n# Install Datasette and its testing dependencies\npython3 -m pip install -e '.[test]' \n That last line does most of the work: pip install -e means \"install this package in a way that allows me to edit the source code in place\". The .[test] option means \"use the setup.py in this directory and install the optional testing dependencies as well\".", "breadcrumbs": "[\"Contributing\"]", "references": "[{\"href\": \"https://docs.python-guide.org/starting/install3/osx/\", \"label\": \"is using homebrew\"}, {\"href\": \"https://github.com/simonw/datasette/fork\", \"label\": \"create a fork of datasette\"}]"} {"id": "contributing:general-guidelines", "page": "contributing", "ref": "general-guidelines", "title": "General guidelines", "content": "main should always be releasable . Incomplete features should live in branches. This ensures that any small bug fixes can be quickly released. \n \n \n The ideal commit should bundle together the implementation, unit tests and associated documentation updates. The commit message should link to an associated issue. \n \n \n New plugin hooks should only be shipped if accompanied by a separate release of a non-demo plugin that uses them.", "breadcrumbs": "[\"Contributing\"]", "references": "[]"} {"id": "contributing:id1", "page": "contributing", "ref": "id1", "title": "Contributing", "content": "Datasette is an open source project. We welcome contributions! \n This document describes how to contribute to Datasette core. You can also contribute to the wider Datasette ecosystem by creating new Plugins .", "breadcrumbs": "[]", "references": "[]"} {"id": "csv_export:csv-export-url-parameters", "page": "csv_export", "ref": "csv-export-url-parameters", "title": "URL parameters", "content": "The following options can be used to customize the CSVs returned by Datasette. \n \n \n ?_header=off \n \n This removes the first row of the CSV file specifying the headings - only the row data will be returned. \n \n \n \n ?_stream=on \n \n Stream all matching records, not just the first page of results. See below. \n \n \n \n ?_dl=on \n \n Causes Datasette to return a content-disposition: attachment; filename=\"filename.csv\" header.", "breadcrumbs": "[\"CSV export\"]", "references": "[]"} {"id": "csv_export:id1", "page": "csv_export", "ref": "id1", "title": "CSV export", "content": "Any Datasette table, view or custom SQL query can be exported as CSV. \n To obtain the CSV representation of the table you are looking, click the \"this\n data as CSV\" link. \n You can also use the advanced export form for more control over the resulting\n file, which looks like this and has the following options: \n \n \n \n download file - instead of displaying CSV in your browser, this forces\n your browser to download the CSV to your downloads directory. \n \n \n expand labels - if your table has any foreign key references this option\n will cause the CSV to gain additional COLUMN_NAME_label columns with a\n label for each foreign key derived from the linked table. In this example \n the city_id column is accompanied by a city_id_label column. \n \n \n stream all rows - by default CSV files only contain the first\n max_returned_rows records. This option will cause Datasette to\n loop through every matching record and return them as a single CSV file. \n \n \n You can try that out on https://latest.datasette.io/fixtures/facetable?_size=4", "breadcrumbs": "[]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/facetable.csv?_labels=on&_size=max\", \"label\": \"In this example\"}, {\"href\": \"https://latest.datasette.io/fixtures/facetable?_size=4\", \"label\": \"https://latest.datasette.io/fixtures/facetable?_size=4\"}]"} {"id": "csv_export:streaming-all-records", "page": "csv_export", "ref": "streaming-all-records", "title": "Streaming all records", "content": "The stream all rows option is designed to be as efficient as possible -\n under the hood it takes advantage of Python 3 asyncio capabilities and\n Datasette's efficient pagination to stream back the full\n CSV file. \n Since databases can get pretty large, by default this option is capped at 100MB -\n if a table returns more than 100MB of data the last line of the CSV will be a\n truncation error message. \n You can increase or remove this limit using the max_csv_mb config\n setting. You can also disable the CSV export feature entirely using\n allow_csv_stream .", "breadcrumbs": "[\"CSV export\"]", "references": "[]"} {"id": "custom_templates:css-classes-on-the-body", "page": "custom_templates", "ref": "css-classes-on-the-body", "title": "CSS classes on the ", "content": "Every default template includes CSS classes in the body designed to support\n custom styling. \n The index template (the top level page at / ) gets this: \n \n The database template ( /dbname ) gets this: \n \n The custom SQL template ( /dbname?sql=... ) gets this: \n \n A canned query template ( /dbname/queryname ) gets this: \n \n The table template ( /dbname/tablename ) gets: \n \n The row template ( /dbname/tablename/rowid ) gets: \n \n The db-x and table-x classes use the database or table names themselves if\n they are valid CSS identifiers. If they aren't, we strip any invalid\n characters out and append a 6 character md5 digest of the original name, in\n order to ensure that multiple tables which resolve to the same stripped\n character version still have different CSS classes. \n Some examples: \n \"simple\" => \"simple\"\n\"MixedCase\" => \"MixedCase\"\n\"-no-leading-hyphens\" => \"no-leading-hyphens-65bea6\"\n\"_no-leading-underscores\" => \"no-leading-underscores-b921bc\"\n\"no spaces\" => \"no-spaces-7088d7\"\n\"-\" => \"336d5e\"\n\"no $ characters\" => \"no--characters-59e024\" \n and elements also get custom CSS classes reflecting the\n database column they are representing, for example: \n \n \n \n \n \n \n \n \n \n \n \n \n \n
idname
1SMITH
", "breadcrumbs": "[\"Custom pages and templates\"]", "references": "[]"} {"id": "custom_templates:custom-pages-404", "page": "custom_templates", "ref": "custom-pages-404", "title": "Returning 404s", "content": "To indicate that content could not be found and display the default 404 page you can use the raise_404(message) function: \n {% if not rows %}\n {{ raise_404(\"Content not found\") }}\n{% endif %} \n If you call raise_404() the other content in your template will be ignored.", "breadcrumbs": "[\"Custom pages and templates\"]", "references": "[]"} {"id": "custom_templates:custom-pages-errors", "page": "custom_templates", "ref": "custom-pages-errors", "title": "Custom error pages", "content": "Datasette returns an error page if an unexpected error occurs, access is forbidden or content cannot be found. \n You can customize the response returned for these errors by providing a custom error page template. \n Content not found errors use a 404.html template. Access denied errors use 403.html . Invalid input errors use 400.html . Unexpected errors of other kinds use 500.html . \n If a template for the specific error code is not found a template called error.html will be used instead. If you do not provide that template Datasette's default error.html template will be used. \n The error template will be passed the following context: \n \n \n status - integer \n \n The integer HTTP status code, e.g. 404, 500, 403, 400. \n \n \n \n error - string \n \n Details of the specific error, usually a full sentence. \n \n \n \n title - string or None \n \n A title for the page representing the class of error. This is often None for errors that do not provide a title separate from their error message.", "breadcrumbs": "[\"Custom pages and templates\", \"Custom redirects\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/blob/main/datasette/templates/error.html\", \"label\": \"default error.html template\"}]"} {"id": "custom_templates:custom-pages-headers", "page": "custom_templates", "ref": "custom-pages-headers", "title": "Custom headers and status codes", "content": "Custom pages default to being served with a content-type of text/html; charset=utf-8 and a 200 status code. You can change these by calling a custom function from within your template. \n For example, to serve a custom page with a 418 I'm a teapot HTTP status code, create a file in pages/teapot.html containing the following: \n {{ custom_status(418) }}\n\nTeapot\n\nI'm a teapot\n\n \n To serve a custom HTTP header, add a custom_header(name, value) function call. For example: \n {{ custom_status(418) }}\n{{ custom_header(\"x-teapot\", \"I am\") }}\n\nTeapot\n\nI'm a teapot\n\n \n You can verify this is working using curl like this: \n curl -I 'http://127.0.0.1:8001/teapot'\nHTTP/1.1 418\ndate: Sun, 26 Apr 2020 18:38:30 GMT\nserver: uvicorn\nx-teapot: I am\ncontent-type: text/html; charset=utf-8", "breadcrumbs": "[\"Custom pages and templates\"]", "references": "[]"} {"id": "custom_templates:custom-pages-parameters", "page": "custom_templates", "ref": "custom-pages-parameters", "title": "Path parameters for pages", "content": "You can define custom pages that match multiple paths by creating files with {variable} definitions in their filenames. \n For example, to capture any request to a URL matching /about/* , you would create a template in the following location: \n templates/pages/about/{slug}.html \n A hit to /about/news would render that template and pass in a variable called slug with a value of \"news\" . \n If you use this mechanism don't forget to return a 404 if the referenced content could not be found. You can do this using {{ raise_404() }} described below. \n Templates defined using custom page routes work particularly well with the sql() template function from datasette-template-sql or the graphql() template function from datasette-graphql .", "breadcrumbs": "[\"Custom pages and templates\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette-template-sql\", \"label\": \"datasette-template-sql\"}, {\"href\": \"https://github.com/simonw/datasette-graphql#the-graphql-template-function\", \"label\": \"datasette-graphql\"}]"} {"id": "custom_templates:custom-pages-redirects", "page": "custom_templates", "ref": "custom-pages-redirects", "title": "Custom redirects", "content": "You can use the custom_redirect(location) function to redirect users to another page, for example in a file called pages/datasette.html : \n {{ custom_redirect(\"https://github.com/simonw/datasette\") }} \n Now requests to http://localhost:8001/datasette will result in a redirect. \n These redirects are served with a 302 Found status code by default. You can send a 301 Moved Permanently code by passing 301 as the second argument to the function: \n {{ custom_redirect(\"https://github.com/simonw/datasette\", 301) }}", "breadcrumbs": "[\"Custom pages and templates\"]", "references": "[]"} {"id": "custom_templates:customization", "page": "custom_templates", "ref": "customization", "title": "Custom pages and templates", "content": "Datasette provides a number of ways of customizing the way data is displayed.", "breadcrumbs": "[]", "references": "[]"} {"id": "custom_templates:customization-custom-templates", "page": "custom_templates", "ref": "customization-custom-templates", "title": "Custom templates", "content": "By default, Datasette uses default templates that ship with the package. \n You can over-ride these templates by specifying a custom --template-dir like\n this: \n datasette mydb.db --template-dir=mytemplates/ \n Datasette will now first look for templates in that directory, and fall back on\n the defaults if no matches are found. \n It is also possible to over-ride templates on a per-database, per-row or per-\n table basis. \n The lookup rules Datasette uses are as follows: \n Index page (/):\n index.html\n\nDatabase page (/mydatabase):\n database-mydatabase.html\n database.html\n\nCustom query page (/mydatabase?sql=...):\n query-mydatabase.html\n query.html\n\nCanned query page (/mydatabase/canned-query):\n query-mydatabase-canned-query.html\n query-mydatabase.html\n query.html\n\nTable page (/mydatabase/mytable):\n table-mydatabase-mytable.html\n table.html\n\nRow page (/mydatabase/mytable/id):\n row-mydatabase-mytable.html\n row.html\n\nTable of rows and columns include on table page:\n _table-table-mydatabase-mytable.html\n _table-mydatabase-mytable.html\n _table.html\n\nTable of rows and columns include on row page:\n _table-row-mydatabase-mytable.html\n _table-mydatabase-mytable.html\n _table.html \n If a table name has spaces or other unexpected characters in it, the template\n filename will follow the same rules as our custom CSS classes - for\n example, a table called \"Food Trucks\" will attempt to load the following\n templates: \n table-mydatabase-Food-Trucks-399138.html\ntable.html \n You can find out which templates were considered for a specific page by viewing\n source on that page and looking for an HTML comment at the bottom. The comment\n will look something like this: \n \n This example is from the canned query page for a query called \"tz\" in the\n database called \"mydb\". The asterisk shows which template was selected - so in\n this case, Datasette found a template file called query-mydb-tz.html and\n used that - but if that template had not been found, it would have tried for\n query-mydb.html or the default query.html . \n It is possible to extend the default templates using Jinja template\n inheritance. If you want to customize EVERY row template with some additional\n content you can do so by creating a row.html template like this: \n {% extends \"default:row.html\" %}\n\n{% block content %}\n

EXTRA HTML AT THE TOP OF THE CONTENT BLOCK

\n

This line renders the original block:

\n{{ super() }}\n{% endblock %} \n Note the default:row.html template name, which ensures Jinja will inherit\n from the default template. \n The _table.html template is included by both the row and the table pages,\n and a list of rows. The default _table.html template renders them as an\n HTML template and can be seen here . \n You can provide a custom template that applies to all of your databases and\n tables, or you can provide custom templates for specific tables using the\n template naming scheme described above. \n If you want to present your data in a format other than an HTML table, you\n can do so by looping through display_rows in your own _table.html \n template. You can use {{ row[\"column_name\"] }} to output the raw value\n of a specific column. \n If you want to output the rendered HTML version of a column, including any\n links to foreign keys, you can use {{ row.display(\"column_name\") }} . \n Here is an example of a custom _table.html template: \n {% for row in display_rows %}\n
\n

{{ row[\"title\"] }}

\n

{{ row[\"description\"] }}\n

Category: {{ row.display(\"category_id\") }}

\n
\n{% endfor %}", "breadcrumbs": "[\"Custom pages and templates\", \"Publishing static assets\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/blob/main/datasette/templates/_table.html\", \"label\": \"can be seen here\"}]"} {"id": "custom_templates:customization-static-files", "page": "custom_templates", "ref": "customization-static-files", "title": "Serving static files", "content": "Datasette can serve static files for you, using the --static option.\n Consider the following directory structure: \n metadata.json\nstatic-files/styles.css\nstatic-files/app.js \n You can start Datasette using --static assets:static-files/ to serve those\n files from the /assets/ mount point: \n datasette --config datasette.yaml --static assets:static-files/ --memory \n The following URLs will now serve the content from those CSS and JS files: \n http://localhost:8001/assets/styles.css\nhttp://localhost:8001/assets/app.js \n You can reference those files from datasette.yaml like this, see custom CSS and JavaScript for more details: \n [[[cog\nfrom metadata_doc import config_example\nconfig_example(cog, \"\"\"\n extra_css_urls:\n - /assets/styles.css\n extra_js_urls:\n - /assets/app.js\n\"\"\") \n ]]] \n [[[end]]]", "breadcrumbs": "[\"Custom pages and templates\"]", "references": "[]"} {"id": "custom_templates:id1", "page": "custom_templates", "ref": "id1", "title": "Custom pages", "content": "You can add templated pages to your Datasette instance by creating HTML files in a pages directory within your templates directory. \n For example, to add a custom page that is served at http://localhost/about you would create a file in templates/pages/about.html , then start Datasette like this: \n datasette mydb.db --template-dir=templates/ \n You can nest directories within pages to create a nested structure. To create a http://localhost:8001/about/map page you would create templates/pages/about/map.html .", "breadcrumbs": "[\"Custom pages and templates\", \"Publishing static assets\"]", "references": "[]"} {"id": "custom_templates:publishing-static-assets", "page": "custom_templates", "ref": "publishing-static-assets", "title": "Publishing static assets", "content": "The datasette publish command can be used to publish your static assets,\n using the same syntax as above: \n datasette publish cloudrun mydb.db --static assets:static-files/ \n This will upload the contents of the static-files/ directory as part of the\n deployment, and configure Datasette to correctly serve the assets from /assets/ .", "breadcrumbs": "[\"Custom pages and templates\"]", "references": "[]"} {"id": "deploying:apache-proxy-configuration", "page": "deploying", "ref": "apache-proxy-configuration", "title": "Apache proxy configuration", "content": "For Apache , you can use the ProxyPass directive. First make sure the following lines are uncommented: \n LoadModule proxy_module lib/httpd/modules/mod_proxy.so\nLoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so \n Then add these directives to proxy traffic: \n ProxyPass /my-datasette/ http://127.0.0.1:8009/my-datasette/\nProxyPreserveHost On \n A live demo of Datasette running behind Apache using this proxy setup can be seen at datasette-apache-proxy-demo.datasette.io/prefix/ . The code for that demo can be found in the demos/apache-proxy directory. \n Using --uds you can use Unix domain sockets similar to the nginx example: \n ProxyPass /my-datasette/ unix:/tmp/datasette.sock|http://localhost/my-datasette/ \n The ProxyPreserveHost On directive ensures that the original Host: header from the incoming request is passed through to Datasette. Datasette needs this to correctly assemble links to other pages using the .absolute_url(request, path) method.", "breadcrumbs": "[\"Deploying Datasette\", \"Running Datasette behind a proxy\"]", "references": "[{\"href\": \"https://httpd.apache.org/\", \"label\": \"Apache\"}, {\"href\": \"https://datasette-apache-proxy-demo.datasette.io/prefix/\", \"label\": \"datasette-apache-proxy-demo.datasette.io/prefix/\"}, {\"href\": \"https://github.com/simonw/datasette/tree/main/demos/apache-proxy\", \"label\": \"demos/apache-proxy\"}, {\"href\": \"https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#proxypreservehost\", \"label\": \"ProxyPreserveHost On\"}]"} {"id": "deploying:deploying", "page": "deploying", "ref": "deploying", "title": "Deploying Datasette", "content": "The quickest way to deploy a Datasette instance on the internet is to use the datasette publish command, described in Publishing data . This can be used to quickly deploy Datasette to a number of hosting providers including Heroku, Google Cloud Run and Vercel. \n You can deploy Datasette to other hosting providers using the instructions on this page.", "breadcrumbs": "[]", "references": "[]"} {"id": "deploying:deploying-buildpacks", "page": "deploying", "ref": "deploying-buildpacks", "title": "Deploying using buildpacks", "content": "Some hosting providers such as Heroku , DigitalOcean App Platform and Scalingo support the Buildpacks standard for deploying Python web applications. \n Deploying Datasette on these platforms requires two files: requirements.txt and Procfile . \n The requirements.txt file lets the platform know which Python packages should be installed. It should contain datasette at a minimum, but can also list any Datasette plugins you wish to install - for example: \n datasette\ndatasette-vega \n The Procfile lets the hosting platform know how to run the command that serves web traffic. It should look like this: \n web: datasette . -h 0.0.0.0 -p $PORT --cors \n The $PORT environment variable is provided by the hosting platform. --cors enables CORS requests from JavaScript running on other websites to your domain - omit this if you don't want to allow CORS. You can add additional Datasette Settings options here too. \n These two files should be enough to deploy Datasette on any host that supports buildpacks. Datasette will serve any SQLite files that are included in the root directory of the application. \n If you want to build SQLite files or download them as part of the deployment process you can do so using a bin/post_compile file. For example, the following bin/post_compile will download an example database that will then be served by Datasette: \n wget https://fivethirtyeight.datasettes.com/fivethirtyeight.db \n simonw/buildpack-datasette-demo is an example GitHub repository showing a Datasette configuration that can be deployed to a buildpack-supporting host.", "breadcrumbs": "[\"Deploying Datasette\"]", "references": "[{\"href\": \"https://www.heroku.com/\", \"label\": \"Heroku\"}, {\"href\": \"https://www.digitalocean.com/docs/app-platform/\", \"label\": \"DigitalOcean App Platform\"}, {\"href\": \"https://scalingo.com/\", \"label\": \"Scalingo\"}, {\"href\": \"https://buildpacks.io/\", \"label\": \"Buildpacks standard\"}, {\"href\": \"https://github.com/simonw/buildpack-datasette-demo\", \"label\": \"simonw/buildpack-datasette-demo\"}]"} {"id": "deploying:deploying-fundamentals", "page": "deploying", "ref": "deploying-fundamentals", "title": "Deployment fundamentals", "content": "Datasette can be deployed as a single datasette process that listens on a port. Datasette is not designed to be run as root, so that process should listen on a higher port such as port 8000. \n If you want to serve Datasette on port 80 (the HTTP default port) or port 443 (for HTTPS) you should run it behind a proxy server, such as nginx, Apache or HAProxy. The proxy server can listen on port 80/443 and forward traffic on to Datasette.", "breadcrumbs": "[\"Deploying Datasette\"]", "references": "[]"} {"id": "deploying:deploying-openrc", "page": "deploying", "ref": "deploying-openrc", "title": "Running Datasette using OpenRC", "content": "OpenRC is the service manager on non-systemd Linux distributions like Alpine Linux and Gentoo . \n Create an init script at /etc/init.d/datasette with the following contents: \n #!/sbin/openrc-run\n\nname=\"datasette\"\ncommand=\"datasette\"\ncommand_args=\"serve -h 0.0.0.0 /path/to/db.db\"\ncommand_background=true\npidfile=\"/run/${RC_SVCNAME}.pid\" \n You then need to configure the service to run at boot and start it: \n rc-update add datasette\nrc-service datasette start", "breadcrumbs": "[\"Deploying Datasette\"]", "references": "[{\"href\": \"https://www.alpinelinux.org/\", \"label\": \"Alpine Linux\"}, {\"href\": \"https://www.gentoo.org/\", \"label\": \"Gentoo\"}]"} {"id": "deploying:deploying-proxy", "page": "deploying", "ref": "deploying-proxy", "title": "Running Datasette behind a proxy", "content": "You may wish to run Datasette behind an Apache or nginx proxy, using a path within your existing site. \n You can use the base_url configuration setting to tell Datasette to serve traffic with a specific URL prefix. For example, you could run Datasette like this: \n datasette my-database.db --setting base_url /my-datasette/ -p 8009 \n This will run Datasette with the following URLs: \n \n \n http://127.0.0.1:8009/my-datasette/ - the Datasette homepage \n \n \n http://127.0.0.1:8009/my-datasette/my-database - the page for the my-database.db database \n \n \n http://127.0.0.1:8009/my-datasette/my-database/some_table - the page for the some_table table \n \n \n You can now set your nginx or Apache server to proxy the /my-datasette/ path to this Datasette instance.", "breadcrumbs": "[\"Deploying Datasette\"]", "references": "[]"} {"id": "deploying:deploying-systemd", "page": "deploying", "ref": "deploying-systemd", "title": "Running Datasette using systemd", "content": "You can run Datasette on Ubuntu or Debian systems using systemd . \n First, ensure you have Python 3 and pip installed. On Ubuntu you can use sudo apt-get install python3 python3-pip . \n You can install Datasette into a virtual environment, or you can install it system-wide. To install system-wide, use sudo pip3 install datasette . \n Now create a folder for your Datasette databases, for example using mkdir /home/ubuntu/datasette-root . \n You can copy a test database into that folder like so: \n cd /home/ubuntu/datasette-root\ncurl -O https://latest.datasette.io/fixtures.db \n Create a file at /etc/systemd/system/datasette.service with the following contents: \n [Unit]\nDescription=Datasette\nAfter=network.target\n\n[Service]\nType=simple\nUser=ubuntu\nEnvironment=DATASETTE_SECRET=\nWorkingDirectory=/home/ubuntu/datasette-root\nExecStart=datasette serve . -h 127.0.0.1 -p 8000\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target \n Add a random value for the DATASETTE_SECRET - this will be used to sign Datasette cookies such as the CSRF token cookie. You can generate a suitable value like so: \n python3 -c 'import secrets; print(secrets.token_hex(32))' \n This configuration will run Datasette against all database files contained in the /home/ubuntu/datasette-root directory. If that directory contains a metadata.yml (or .json ) file or a templates/ or plugins/ sub-directory those will automatically be loaded by Datasette - see Configuration directory mode for details. \n You can start the Datasette process running using the following: \n sudo systemctl daemon-reload\nsudo systemctl start datasette.service \n You will need to restart the Datasette service after making changes to its metadata.json configuration or adding a new database file to that directory. You can do that using: \n sudo systemctl restart datasette.service \n Once the service has started you can confirm that Datasette is running on port 8000 like so: \n curl 127.0.0.1:8000/-/versions.json\n# Should output JSON showing the installed version \n Datasette will not be accessible from outside the server because it is listening on 127.0.0.1 . You can expose it by instead listening on 0.0.0.0 , but a better way is to set up a proxy such as nginx - see Running Datasette behind a proxy .", "breadcrumbs": "[\"Deploying Datasette\"]", "references": "[]"} {"id": "deploying:nginx-proxy-configuration", "page": "deploying", "ref": "nginx-proxy-configuration", "title": "Nginx proxy configuration", "content": "Here is an example of an nginx configuration file that will proxy traffic to Datasette: \n daemon off;\n\nevents {\n worker_connections 1024;\n}\nhttp {\n server {\n listen 80;\n location /my-datasette {\n proxy_pass http://127.0.0.1:8009/my-datasette;\n proxy_set_header Host $host;\n }\n }\n} \n You can also use the --uds option to Datasette to listen on a Unix domain socket instead of a port, configuring the nginx upstream proxy like this: \n daemon off;\nevents {\n worker_connections 1024;\n}\nhttp {\n server {\n listen 80;\n location /my-datasette {\n proxy_pass http://datasette/my-datasette;\n proxy_set_header Host $host;\n }\n }\n upstream datasette {\n server unix:/tmp/datasette.sock;\n }\n} \n Then run Datasette with datasette --uds /tmp/datasette.sock path/to/database.db --setting base_url /my-datasette/ .", "breadcrumbs": "[\"Deploying Datasette\", \"Running Datasette behind a proxy\"]", "references": "[{\"href\": \"https://nginx.org/\", \"label\": \"nginx\"}]"} {"id": "ecosystem:dogsheep", "page": "ecosystem", "ref": "dogsheep", "title": "Dogsheep", "content": "Dogsheep is a collection of tools for personal analytics using SQLite and Datasette. The project provides tools like github-to-sqlite and twitter-to-sqlite that can import data from different sources in order to create a personal data warehouse. Personal Data Warehouses: Reclaiming Your Data is a talk that explains Dogsheep and demonstrates it in action.", "breadcrumbs": "[\"The Datasette Ecosystem\"]", "references": "[{\"href\": \"https://dogsheep.github.io/\", \"label\": \"Dogsheep\"}, {\"href\": \"https://datasette.io/tools/github-to-sqlite\", \"label\": \"github-to-sqlite\"}, {\"href\": \"https://datasette.io/tools/twitter-to-sqlite\", \"label\": \"twitter-to-sqlite\"}, {\"href\": \"https://simonwillison.net/2020/Nov/14/personal-data-warehouses/\", \"label\": \"Personal Data Warehouses: Reclaiming Your Data\"}]"} {"id": "ecosystem:ecosystem", "page": "ecosystem", "ref": "ecosystem", "title": "The Datasette Ecosystem", "content": "Datasette sits at the center of a growing ecosystem of open source tools aimed at making it as easy as possible to gather, analyze and publish interesting data. \n These tools are divided into two main groups: tools for building SQLite databases (for use with Datasette) and plugins that extend Datasette's functionality. \n The Datasette project website includes a directory of plugins and a directory of tools: \n \n \n Plugins directory on datasette.io \n \n \n Tools directory on datasette.io", "breadcrumbs": "[]", "references": "[{\"href\": \"https://datasette.io/\", \"label\": \"Datasette project website\"}, {\"href\": \"https://datasette.io/plugins\", \"label\": \"Plugins directory on datasette.io\"}, {\"href\": \"https://datasette.io/tools\", \"label\": \"Tools directory on datasette.io\"}]"} {"id": "ecosystem:sqlite-utils", "page": "ecosystem", "ref": "sqlite-utils", "title": "sqlite-utils", "content": "sqlite-utils is a key building block for the wider Datasette ecosystem. It provides a collection of utilities for manipulating SQLite databases, both as a Python library and a command-line utility. Features include: \n \n \n Insert data into a SQLite database from JSON, CSV or TSV, automatically creating tables with the correct schema or altering existing tables to add missing columns. \n \n \n Configure tables for use with SQLite full-text search, including creating triggers needed to keep the search index up-to-date. \n \n \n Modify tables in ways that are not supported by SQLite's default ALTER TABLE syntax - for example changing the types of columns or selecting a new primary key for a table. \n \n \n Adding foreign keys to existing database tables. \n \n \n Extracting columns of data into a separate lookup table.", "breadcrumbs": "[\"The Datasette Ecosystem\"]", "references": "[{\"href\": \"https://sqlite-utils.datasette.io/\", \"label\": \"sqlite-utils\"}]"} {"id": "events:id1", "page": "events", "ref": "id1", "title": "Events", "content": "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. \n The core Datasette application triggers events when certain things happen. This page describes those events. \n 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 . \n \n \n \n \n class datasette.events. LoginEvent actor : dict | None \n \n Event name: login \n A user (represented by event.actor ) has logged in. \n \n \n \n \n class datasette.events. LogoutEvent actor : dict | None \n \n Event name: logout \n A user (represented by event.actor ) has logged out. \n \n \n \n \n class datasette.events. CreateTokenEvent actor : dict | None expires_after : int | None restrict_all : list restrict_database : dict restrict_resource : dict \n \n Event name: create-token \n A user created an API token. \n \n \n Variables \n \n \n \n expires_after -- Number of seconds after which this token will expire. \n \n \n restrict_all -- Restricted permissions for this token. \n \n \n restrict_database -- Restricted database permissions for this token. \n \n \n restrict_resource -- Restricted resource permissions for this token. \n \n \n \n \n \n \n \n \n \n class datasette.events. CreateTableEvent actor : dict | None database : str table : str schema : str \n \n Event name: create-table \n A new table has been created in the database. \n \n \n Variables \n \n \n \n database -- The name of the database where the table was created. \n \n \n table -- The name of the table that was created \n \n \n schema -- The SQL schema definition for the new table. \n \n \n \n \n \n \n \n \n \n class datasette.events. DropTableEvent actor : dict | None database : str table : str \n \n Event name: drop-table \n A table has been dropped from the database. \n \n \n Variables \n \n \n \n database -- The name of the database where the table was dropped. \n \n \n table -- The name of the table that was dropped \n \n \n \n \n \n \n \n \n \n class datasette.events. AlterTableEvent actor : dict | None database : str table : str before_schema : str after_schema : str \n \n Event name: alter-table \n A table has been altered. \n \n \n Variables \n \n \n \n database -- The name of the database where the table was altered \n \n \n table -- The name of the table that was altered \n \n \n before_schema -- The table's SQL schema before the alteration \n \n \n after_schema -- The table's SQL schema after the alteration \n \n \n \n \n \n \n \n \n \n class datasette.events. InsertRowsEvent actor : dict | None database : str table : str num_rows : int ignore : bool replace : bool \n \n Event name: insert-rows \n Rows were inserted into a table. \n \n \n Variables \n \n \n \n database -- The name of the database where the rows were inserted. \n \n \n table -- The name of the table where the rows were inserted. \n \n \n num_rows -- The number of rows that were requested to be inserted. \n \n \n ignore -- Was ignore set? \n \n \n replace -- Was replace set? \n \n \n \n \n \n \n \n \n \n class datasette.events. UpsertRowsEvent actor : dict | None database : str table : str num_rows : int \n \n Event name: upsert-rows \n Rows were upserted into a table. \n \n \n Variables \n \n \n \n database -- The name of the database where the rows were inserted. \n \n \n table -- The name of the table where the rows were inserted. \n \n \n num_rows -- The number of rows that were requested to be inserted. \n \n \n \n \n \n \n \n \n \n class datasette.events. UpdateRowEvent actor : dict | None database : str table : str pks : list \n \n Event name: update-row \n A row was updated in a table. \n \n \n Variables \n \n \n \n database -- The name of the database where the row was updated. \n \n \n table -- The name of the table where the row was updated. \n \n \n pks -- The primary key values of the updated row. \n \n \n \n \n \n \n \n \n \n class datasette.events. DeleteRowEvent actor : dict | None database : str table : str pks : list \n \n Event name: delete-row \n A row was deleted from a table. \n \n \n Variables \n \n \n \n database -- The name of the database where the row was deleted. \n \n \n table -- The name of the table where the row was deleted. \n \n \n pks -- The primary key values of the deleted row.", "breadcrumbs": "[]", "references": "[]"} {"id": "facets:facets-in-query-strings", "page": "facets", "ref": "facets-in-query-strings", "title": "Facets in query strings", "content": "To turn on faceting for specific columns on a Datasette table view, add one or more _facet=COLUMN parameters to the URL.\n For example, if you want to turn on facets for the city_id and state columns, construct a URL that looks like this: \n /dbname/tablename?_facet=state&_facet=city_id \n This works for both the HTML interface and the .json view.\n When enabled, facets will cause a facet_results block to be added to the JSON output, looking something like this: \n {\n \"state\": {\n \"name\": \"state\",\n \"results\": [\n {\n \"value\": \"CA\",\n \"label\": \"CA\",\n \"count\": 10,\n \"toggle_url\": \"http://...?_facet=city_id&_facet=state&state=CA\",\n \"selected\": false\n },\n {\n \"value\": \"MI\",\n \"label\": \"MI\",\n \"count\": 4,\n \"toggle_url\": \"http://...?_facet=city_id&_facet=state&state=MI\",\n \"selected\": false\n },\n {\n \"value\": \"MC\",\n \"label\": \"MC\",\n \"count\": 1,\n \"toggle_url\": \"http://...?_facet=city_id&_facet=state&state=MC\",\n \"selected\": false\n }\n ],\n \"truncated\": false\n }\n \"city_id\": {\n \"name\": \"city_id\",\n \"results\": [\n {\n \"value\": 1,\n \"label\": \"San Francisco\",\n \"count\": 6,\n \"toggle_url\": \"http://...?_facet=city_id&_facet=state&city_id=1\",\n \"selected\": false\n },\n {\n \"value\": 2,\n \"label\": \"Los Angeles\",\n \"count\": 4,\n \"toggle_url\": \"http://...?_facet=city_id&_facet=state&city_id=2\",\n \"selected\": false\n },\n {\n \"value\": 3,\n \"label\": \"Detroit\",\n \"count\": 4,\n \"toggle_url\": \"http://...?_facet=city_id&_facet=state&city_id=3\",\n \"selected\": false\n },\n {\n \"value\": 4,\n \"label\": \"Memnonia\",\n \"count\": 1,\n \"toggle_url\": \"http://...?_facet=city_id&_facet=state&city_id=4\",\n \"selected\": false\n }\n ],\n \"truncated\": false\n }\n} \n 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. \n The default number of facet results returned is 30, controlled by the default_facet_size setting.\n 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).", "breadcrumbs": "[\"Facets\"]", "references": "[]"} {"id": "facets:facets-metadata", "page": "facets", "ref": "facets-metadata", "title": "Facets in metadata", "content": "You can turn facets on by default for specific tables by adding them to a \"facets\" key in a Datasette Metadata file. \n 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: \n [[[cog\nfrom metadata_doc import metadata_example\nmetadata_example(cog, {\n \"databases\": {\n \"sf-trees\": {\n \"tables\": {\n \"Street_Tree_List\": {\n \"facets\": [\"qLegalStatus\"]\n }\n }\n }\n }\n}) \n ]]] \n [[[end]]] \n 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. \n You can specify array or date facets in metadata using JSON objects with a single key of array or date and a value specifying the column, like this: \n [[[cog\nmetadata_example(cog, {\n \"facets\": [\n {\"array\": \"tags\"},\n {\"date\": \"created\"}\n ]\n}) \n ]]] \n [[[end]]] \n You can change the default facet size (the number of results shown for each facet) for a table using facet_size : \n [[[cog\nmetadata_example(cog, {\n \"databases\": {\n \"sf-trees\": {\n \"tables\": {\n \"Street_Tree_List\": {\n \"facets\": [\"qLegalStatus\"],\n \"facet_size\": 10\n }\n }\n }\n }\n}) \n ]]] \n [[[end]]]", "breadcrumbs": "[\"Facets\"]", "references": "[]"} {"id": "facets:id1", "page": "facets", "ref": "id1", "title": "Facets", "content": "Datasette facets can be used to add a faceted browse interface to any database table.\n With facets, tables are displayed along with a summary showing the most common values in specified columns.\n These values can be selected to further filter the table. \n Here's an example : \n \n Facets can be specified in two ways: using query string parameters, or in metadata.json configuration for the table.", "breadcrumbs": "[]", "references": "[{\"href\": \"https://congress-legislators.datasettes.com/legislators/legislator_terms?_facet=type&_facet=party&_facet=state&_facet_size=10\", \"label\": \"an example\"}]"} {"id": "facets:id2", "page": "facets", "ref": "id2", "title": "Facet by JSON array", "content": "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. \n This is useful for modelling things like tags without needing to break them out into a new table. \n Example here: latest.datasette.io/fixtures/facetable?_facet_array=tags", "breadcrumbs": "[\"Facets\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/facetable?_facet_array=tags\", \"label\": \"latest.datasette.io/fixtures/facetable?_facet_array=tags\"}]"} {"id": "facets:id3", "page": "facets", "ref": "id3", "title": "Facet by date", "content": "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.\n This works especially well against timestamp values such as 2019-03-01 12:44:00 . \n Example here: latest.datasette.io/fixtures/facetable?_facet_date=created", "breadcrumbs": "[\"Facets\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/facetable?_facet_date=created\", \"label\": \"latest.datasette.io/fixtures/facetable?_facet_date=created\"}]"} {"id": "facets:speeding-up-facets-with-indexes", "page": "facets", "ref": "speeding-up-facets-with-indexes", "title": "Speeding up facets with indexes", "content": "The performance of facets can be greatly improved by adding indexes on the columns you wish to facet by.\n 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 : \n sqlite3 mydatabase.db \n SQLite version 3.19.3 2017-06-27 16:48:08\nEnter \".help\" for usage hints.\nsqlite> CREATE INDEX Food_Trucks_state ON Food_Trucks(\"state\"); \n Or using the sqlite-utils command-line utility: \n sqlite-utils create-index mydatabase.db Food_Trucks state", "breadcrumbs": "[\"Facets\"]", "references": "[{\"href\": \"https://sqlite-utils.datasette.io/en/stable/cli.html#creating-indexes\", \"label\": \"sqlite-utils\"}]"} {"id": "facets:suggested-facets", "page": "facets", "ref": "suggested-facets", "title": "Suggested facets", "content": "Datasette's table UI will suggest facets for the user to apply, based on the following criteria: \n For the currently filtered data are there any columns which, if applied as a facet... \n \n \n Will return 30 or less unique options \n \n \n Will return more than one unique option \n \n \n Will return less unique options than the total number of filtered rows \n \n \n And the query used to evaluate this criteria can be completed in under 50ms \n \n \n 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.\n This means suggested facets are unlikely to appear for tables with millions of records in them.", "breadcrumbs": "[\"Facets\"]", "references": "[]"} {"id": "full_text_search:configuring-fts-by-hand", "page": "full_text_search", "ref": "configuring-fts-by-hand", "title": "Configuring FTS by hand", "content": "We recommend using sqlite-utils , but if you want to hand-roll a SQLite full-text search table you can do so using the following SQL. \n To enable full-text search for a table called items that works against the name and description columns, you would run this SQL to create a new items_fts FTS virtual table: \n CREATE VIRTUAL TABLE \"items_fts\" USING FTS4 (\n name,\n description,\n content=\"items\"\n); \n This creates a set of tables to power full-text search against items . The new items_fts table will be detected by Datasette as the fts_table for the items table. \n Creating the table is not enough: you also need to populate it with a copy of the data that you wish to make searchable. You can do that using the following SQL: \n INSERT INTO \"items_fts\" (rowid, name, description)\n SELECT rowid, name, description FROM items; \n If your table has columns that are foreign key references to other tables you can include that data in your full-text search index using a join. Imagine the items table has a foreign key column called category_id which refers to a categories table - you could create a full-text search table like this: \n CREATE VIRTUAL TABLE \"items_fts\" USING FTS4 (\n name,\n description,\n category_name,\n content=\"items\"\n); \n And then populate it like this: \n INSERT INTO \"items_fts\" (rowid, name, description, category_name)\n SELECT items.rowid,\n items.name,\n items.description,\n categories.name\n FROM items JOIN categories ON items.category_id=categories.id; \n You can use this technique to populate the full-text search index from any combination of tables and joins that makes sense for your project.", "breadcrumbs": "[\"Full-text search\", \"Enabling full-text search for a SQLite table\"]", "references": "[{\"href\": \"https://sqlite-utils.datasette.io/\", \"label\": \"sqlite-utils\"}]"} {"id": "full_text_search:configuring-fts-using-csvs-to-sqlite", "page": "full_text_search", "ref": "configuring-fts-using-csvs-to-sqlite", "title": "Configuring FTS using csvs-to-sqlite", "content": "If your data starts out in CSV files, you can use Datasette's companion tool csvs-to-sqlite to convert that file into a SQLite database and enable full-text search on specific columns. For a file called items.csv where you want full-text search to operate against the name and description columns you would run the following: \n csvs-to-sqlite items.csv items.db -f name -f description", "breadcrumbs": "[\"Full-text search\", \"Enabling full-text search for a SQLite table\"]", "references": "[{\"href\": \"https://github.com/simonw/csvs-to-sqlite\", \"label\": \"csvs-to-sqlite\"}]"} {"id": "full_text_search:configuring-fts-using-sqlite-utils", "page": "full_text_search", "ref": "configuring-fts-using-sqlite-utils", "title": "Configuring FTS using sqlite-utils", "content": "sqlite-utils is a CLI utility and Python library for manipulating SQLite databases. You can use it from Python code to configure FTS search, or you can achieve the same goal using the accompanying command-line tool . \n Here's how to use sqlite-utils to enable full-text search for an items table across the name and description columns: \n sqlite-utils enable-fts mydatabase.db items name description", "breadcrumbs": "[\"Full-text search\", \"Enabling full-text search for a SQLite table\"]", "references": "[{\"href\": \"https://sqlite-utils.datasette.io/\", \"label\": \"sqlite-utils\"}, {\"href\": \"https://sqlite-utils.datasette.io/en/latest/python-api.html#enabling-full-text-search\", \"label\": \"it from Python code\"}, {\"href\": \"https://sqlite-utils.datasette.io/en/latest/cli.html#configuring-full-text-search\", \"label\": \"using the accompanying command-line tool\"}]"} {"id": "full_text_search:full-text-search-advanced-queries", "page": "full_text_search", "ref": "full-text-search-advanced-queries", "title": "Advanced SQLite search queries", "content": "SQLite full-text search includes support for a variety of advanced queries , including AND , OR , NOT and NEAR . \n By default Datasette disables these features to ensure they do not cause errors or confusion for users who are not aware of them. You can disable this escaping and use the advanced queries by adding &_searchmode=raw to the table page query string. \n If you want to enable these operators by default for a specific table, you can do so by adding \"searchmode\": \"raw\" to the metadata configuration for that table, see Configuring full-text search for a table or view . \n If that option has been specified in the table metadata but you want to over-ride it and return to the default behavior you can append &_searchmode=escaped to the query string.", "breadcrumbs": "[\"Full-text search\"]", "references": "[{\"href\": \"https://www.sqlite.org/fts5.html#full_text_query_syntax\", \"label\": \"a variety of advanced queries\"}]"} {"id": "full_text_search:full-text-search-custom-sql", "page": "full_text_search", "ref": "full-text-search-custom-sql", "title": "Searches using custom SQL", "content": "You can include full-text search results in custom SQL queries. The general pattern with SQLite search is to run the search as a sub-select that returns rowid values, then include those rowids in another part of the query. \n You can see the syntax for a basic search by running that search on a table page and then clicking \"View and edit SQL\" to see the underlying SQL. For example, consider this search for manafort is the US FARA database : \n /fara/FARA_All_ShortForms?_search=manafort \n If you click View and edit SQL you'll see that the underlying SQL looks like this: \n select\n rowid,\n Short_Form_Termination_Date,\n Short_Form_Date,\n Short_Form_Last_Name,\n Short_Form_First_Name,\n Registration_Number,\n Registration_Date,\n Registrant_Name,\n Address_1,\n Address_2,\n City,\n State,\n Zip\nfrom\n FARA_All_ShortForms\nwhere\n rowid in (\n select\n rowid\n from\n FARA_All_ShortForms_fts\n where\n FARA_All_ShortForms_fts match escape_fts(:search)\n )\norder by\n rowid\nlimit\n 101", "breadcrumbs": "[\"Full-text search\"]", "references": "[{\"href\": \"https://fara.datasettes.com/fara/FARA_All_ShortForms?_search=manafort\", \"label\": \"manafort is the US FARA database\"}, {\"href\": \"https://fara.datasettes.com/fara?sql=select%0D%0A++rowid%2C%0D%0A++Short_Form_Termination_Date%2C%0D%0A++Short_Form_Date%2C%0D%0A++Short_Form_Last_Name%2C%0D%0A++Short_Form_First_Name%2C%0D%0A++Registration_Number%2C%0D%0A++Registration_Date%2C%0D%0A++Registrant_Name%2C%0D%0A++Address_1%2C%0D%0A++Address_2%2C%0D%0A++City%2C%0D%0A++State%2C%0D%0A++Zip%0D%0Afrom%0D%0A++FARA_All_ShortForms%0D%0Awhere%0D%0A++rowid+in+%28%0D%0A++++select%0D%0A++++++rowid%0D%0A++++from%0D%0A++++++FARA_All_ShortForms_fts%0D%0A++++where%0D%0A++++++FARA_All_ShortForms_fts+match+escape_fts%28%3Asearch%29%0D%0A++%29%0D%0Aorder+by%0D%0A++rowid%0D%0Alimit%0D%0A++101&search=manafort\", \"label\": \"View and edit SQL\"}]"} {"id": "full_text_search:full-text-search-enabling", "page": "full_text_search", "ref": "full-text-search-enabling", "title": "Enabling full-text search for a SQLite table", "content": "Datasette takes advantage of the external content mechanism in SQLite, which allows a full-text search virtual table to be associated with the contents of another SQLite table. \n To set up full-text search for a table, you need to do two things: \n \n \n Create a new FTS virtual table associated with your table \n \n \n Populate that FTS table with the data that you would like to be able to run searches against", "breadcrumbs": "[\"Full-text search\"]", "references": "[{\"href\": \"https://www.sqlite.org/fts3.html#_external_content_fts4_tables_\", \"label\": \"external content\"}]"} {"id": "full_text_search:full-text-search-fts-versions", "page": "full_text_search", "ref": "full-text-search-fts-versions", "title": "FTS versions", "content": "There are three different versions of the SQLite FTS module: FTS3, FTS4 and FTS5. You can tell which versions are supported by your instance of Datasette by checking the /-/versions page. \n FTS5 is the most advanced module but may not be available in the SQLite version that is bundled with your Python installation. Most importantly, FTS5 is the only version that has the ability to order by search relevance without needing extra code. \n If you can't be sure that FTS5 will be available, you should use FTS4.", "breadcrumbs": "[\"Full-text search\"]", "references": "[]"} {"id": "full_text_search:full-text-search-table-or-view", "page": "full_text_search", "ref": "full-text-search-table-or-view", "title": "Configuring full-text search for a table or view", "content": "If a table has a corresponding FTS table set up using the content= argument to CREATE VIRTUAL TABLE shown below, Datasette will detect it automatically and add a search interface to the table page for that table. \n You can also manually configure which table should be used for full-text search using query string parameters or Metadata . You can set the associated FTS table for a specific table and you can also set one for a view - if you do that, the page for that SQL view will offer a search option. \n Use ?_fts_table=x to over-ride the FTS table for a specific page. If the primary key was something other than rowid you can use ?_fts_pk=col to set that as well. This is particularly useful for views, for example: \n https://latest.datasette.io/fixtures/searchable_view?_fts_table=searchable_fts&_fts_pk=pk \n The fts_table metadata property can be used to specify an associated FTS table. If the primary key column in your table which was used to populate the FTS table is something other than rowid , you can specify the column to use with the fts_pk property. \n The \"searchmode\": \"raw\" property can be used to default the table to accepting SQLite advanced search operators, as described in Advanced SQLite search queries . \n Here is an example which enables full-text search (with SQLite advanced search operators) for a display_ads view which is defined against the ads table and hence needs to run FTS against the ads_fts table, using the id as the primary key: \n [[[cog\nfrom metadata_doc import metadata_example\nmetadata_example(cog, {\n \"databases\": {\n \"russian-ads\": {\n \"tables\": {\n \"display_ads\": {\n \"fts_table\": \"ads_fts\",\n \"fts_pk\": \"id\",\n \"searchmode\": \"raw\"\n }\n }\n }\n }\n}) \n ]]] \n [[[end]]]", "breadcrumbs": "[\"Full-text search\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/searchable_view?_fts_table=searchable_fts&_fts_pk=pk\", \"label\": \"https://latest.datasette.io/fixtures/searchable_view?_fts_table=searchable_fts&_fts_pk=pk\"}]"} {"id": "full_text_search:full-text-search-table-view-api", "page": "full_text_search", "ref": "full-text-search-table-view-api", "title": "The table page and table view API", "content": "Table views that support full-text search can be queried using the ?_search=TERMS query string parameter. This will run the search against content from all of the columns that have been included in the index. \n Try this example: fara.datasettes.com/fara/FARA_All_ShortForms?_search=manafort \n SQLite full-text search supports wildcards. This means you can easily implement prefix auto-complete by including an asterisk at the end of the search term - for example: \n /dbname/tablename/?_search=rob* \n This will return all records containing at least one word that starts with the letters rob . \n You can also run searches against just the content of a specific named column by using _search_COLNAME=TERMS - for example, this would search for just rows where the name column in the FTS index mentions Sarah : \n /dbname/tablename/?_search_name=Sarah", "breadcrumbs": "[\"Full-text search\"]", "references": "[{\"href\": \"https://fara.datasettes.com/fara/FARA_All_ShortForms?_search=manafort\", \"label\": \"fara.datasettes.com/fara/FARA_All_ShortForms?_search=manafort\"}]"} {"id": "full_text_search:id1", "page": "full_text_search", "ref": "id1", "title": "Full-text search", "content": "SQLite includes a powerful mechanism for enabling full-text search against SQLite records. Datasette can detect if a table has had full-text search configured for it in the underlying database and display a search interface for filtering that table. \n Here's an example search : \n \n Datasette automatically detects which tables have been configured for full-text search.", "breadcrumbs": "[]", "references": "[{\"href\": \"https://www.sqlite.org/fts3.html\", \"label\": \"a powerful mechanism for enabling full-text search\"}, {\"href\": \"https://register-of-members-interests.datasettes.com/regmem/items?_search=hamper&_sort_desc=date\", \"label\": \"an example search\"}]"} {"id": "getting_started:getting-started", "page": "getting_started", "ref": "getting-started", "title": "Getting started", "content": "", "breadcrumbs": "[]", "references": "[]"} {"id": "getting_started:getting-started-datasette-lite", "page": "getting_started", "ref": "getting-started-datasette-lite", "title": "Datasette in your browser with Datasette Lite", "content": "Datasette Lite is Datasette packaged using WebAssembly so that it runs entirely in your browser, no Python web application server required. \n You can pass a URL to a CSV, SQLite or raw SQL file directly to Datasette Lite to explore that data in your browser. \n This example link opens Datasette Lite and loads the SQL Murder Mystery example database from Northwestern University Knight Lab .", "breadcrumbs": "[\"Getting started\"]", "references": "[{\"href\": \"https://lite.datasette.io/\", \"label\": \"Datasette Lite\"}, {\"href\": \"https://lite.datasette.io/?url=https%3A%2F%2Fraw.githubusercontent.com%2FNUKnightLab%2Fsql-mysteries%2Fmaster%2Fsql-murder-mystery.db#/sql-murder-mystery\", \"label\": \"example link\"}, {\"href\": \"https://github.com/NUKnightLab/sql-mysteries\", \"label\": \"Northwestern University Knight Lab\"}]"} {"id": "getting_started:getting-started-demo", "page": "getting_started", "ref": "getting-started-demo", "title": "Play with a live demo", "content": "The best way to experience Datasette for the first time is with a demo: \n \n \n global-power-plants.datasettes.com provides a searchable database of power plants around the world, using data from the World Resources Institude rendered using the datasette-cluster-map plugin. \n \n \n fivethirtyeight.datasettes.com shows Datasette running against over 400 datasets imported from the FiveThirtyEight GitHub repository .", "breadcrumbs": "[\"Getting started\"]", "references": "[{\"href\": \"https://global-power-plants.datasettes.com/global-power-plants/global-power-plants\", \"label\": \"global-power-plants.datasettes.com\"}, {\"href\": \"https://www.wri.org/publication/global-power-plant-database\", \"label\": \"World Resources Institude\"}, {\"href\": \"https://github.com/simonw/datasette-cluster-map\", \"label\": \"datasette-cluster-map\"}, {\"href\": \"https://fivethirtyeight.datasettes.com/fivethirtyeight\", \"label\": \"fivethirtyeight.datasettes.com\"}, {\"href\": \"https://github.com/fivethirtyeight/data\", \"label\": \"FiveThirtyEight GitHub repository\"}]"}