{"id": "authentication:permissions-view-table", "page": "authentication", "ref": "permissions-view-table", "title": "view-table", "content": "Actor is allowed to view a table (or view) page, e.g. https://latest.datasette.io/fixtures/complex_foreign_keys \n \n \n resource - tuple: (string, string) \n \n The name of the database, then the name of the table \n \n \n \n Default allow .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/complex_foreign_keys\", \"label\": \"https://latest.datasette.io/fixtures/complex_foreign_keys\"}]"} {"id": "authentication:permissions-view-query", "page": "authentication", "ref": "permissions-view-query", "title": "view-query", "content": "Actor is allowed to view (and execute) a canned query page, e.g. https://latest.datasette.io/fixtures/pragma_cache_size - this includes executing Writable canned queries . \n \n \n resource - tuple: (string, string) \n \n The name of the database, then the name of the canned query \n \n \n \n Default allow .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/pragma_cache_size\", \"label\": \"https://latest.datasette.io/fixtures/pragma_cache_size\"}]"} {"id": "authentication:permissions-view-instance", "page": "authentication", "ref": "permissions-view-instance", "title": "view-instance", "content": "Top level permission - Actor is allowed to view any pages within this instance, starting at https://latest.datasette.io/ \n Default allow .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[{\"href\": \"https://latest.datasette.io/\", \"label\": \"https://latest.datasette.io/\"}]"} {"id": "authentication:permissions-view-database-download", "page": "authentication", "ref": "permissions-view-database-download", "title": "view-database-download", "content": "Actor is allowed to download a database, e.g. https://latest.datasette.io/fixtures.db \n \n \n resource - string \n \n The name of the database \n \n \n \n Default allow .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures.db\", \"label\": \"https://latest.datasette.io/fixtures.db\"}]"} {"id": "authentication:permissions-view-database", "page": "authentication", "ref": "permissions-view-database", "title": "view-database", "content": "Actor is allowed to view a database page, e.g. https://latest.datasette.io/fixtures \n \n \n resource - string \n \n The name of the database \n \n \n \n Default allow .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures\", \"label\": \"https://latest.datasette.io/fixtures\"}]"} {"id": "settings:setting-truncate-cells-html", "page": "settings", "ref": "setting-truncate-cells-html", "title": "truncate_cells_html", "content": "In the HTML table view, truncate any strings that are longer than this value.\n The full value will still be available in CSV, JSON and on the individual row\n HTML page. Set this to 0 to disable truncation. \n datasette mydatabase.db --setting truncate_cells_html 0", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-trace-debug", "page": "settings", "ref": "setting-trace-debug", "title": "trace_debug", "content": "This setting enables appending ?_trace=1 to any page in order to see the SQL queries and other trace information that was used to generate that page. \n Enable it like this: \n datasette mydatabase.db --setting trace_debug 1 \n Some examples: \n \n \n https://latest.datasette.io/?_trace=1 \n \n \n https://latest.datasette.io/fixtures/roadside_attractions?_trace=1 \n \n \n See datasette.tracer for details on how to hook into this mechanism as a plugin author.", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[{\"href\": \"https://latest.datasette.io/?_trace=1\", \"label\": \"https://latest.datasette.io/?_trace=1\"}, {\"href\": \"https://latest.datasette.io/fixtures/roadside_attractions?_trace=1\", \"label\": \"https://latest.datasette.io/fixtures/roadside_attractions?_trace=1\"}]"} {"id": "settings:setting-template-debug", "page": "settings", "ref": "setting-template-debug", "title": "template_debug", "content": "This setting enables template context debug mode, which is useful to help understand what variables are available to custom templates when you are writing them. \n Enable it like this: \n datasette mydatabase.db --setting template_debug 1 \n Now you can add ?_context=1 or &_context=1 to any Datasette page to see the context that was passed to that template. \n Some examples: \n \n \n https://latest.datasette.io/?_context=1 \n \n \n https://latest.datasette.io/fixtures?_context=1 \n \n \n https://latest.datasette.io/fixtures/roadside_attractions?_context=1", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[{\"href\": \"https://latest.datasette.io/?_context=1\", \"label\": \"https://latest.datasette.io/?_context=1\"}, {\"href\": \"https://latest.datasette.io/fixtures?_context=1\", \"label\": \"https://latest.datasette.io/fixtures?_context=1\"}, {\"href\": \"https://latest.datasette.io/fixtures/roadside_attractions?_context=1\", \"label\": \"https://latest.datasette.io/fixtures/roadside_attractions?_context=1\"}]"} {"id": "plugin_hooks:plugin-hook-table-actions", "page": "plugin_hooks", "ref": "plugin-hook-table-actions", "title": "table_actions(datasette, actor, database, table, request)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n actor - dictionary or None \n \n The currently authenticated actor . \n \n \n \n database - string \n \n The name of the database. \n \n \n \n table - string \n \n The name of the table. \n \n \n \n request - Request object or None \n \n The current HTTP request. This can be None if the request object is not available. \n \n \n \n This hook allows table actions to be displayed in a menu accessed via an action icon at the top of the table page. It should return a list of {\"href\": \"...\", \"label\": \"...\"} menu items. \n It can alternatively return an async def awaitable function which returns a list of menu items. \n This example adds a new table action if the signed in user is \"root\" : \n from datasette import hookimpl\n\n\n@hookimpl\ndef table_actions(datasette, actor, database, table):\n if actor and actor.get(\"id\") == \"root\":\n return [\n {\n \"href\": datasette.urls.path(\n \"/-/edit-schema/{}/{}\".format(\n database, table\n )\n ),\n \"label\": \"Edit schema for this table\",\n }\n ] \n Example: datasette-graphql", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-graphql\", \"label\": \"datasette-graphql\"}]"} {"id": "settings:setting-suggest-facets", "page": "settings", "ref": "setting-suggest-facets", "title": "suggest_facets", "content": "Should Datasette calculate suggested facets? On by default, turn this off like so: \n datasette mydatabase.db --setting suggest_facets off", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-startup", "page": "plugin_hooks", "ref": "plugin-hook-startup", "title": "startup(datasette)", "content": "This hook fires when the Datasette application server first starts up. You can implement a regular function, for example to validate required plugin configuration: \n @hookimpl\ndef startup(datasette):\n config = datasette.plugin_config(\"my-plugin\") or {}\n assert (\n \"required-setting\" in config\n ), \"my-plugin requires setting required-setting\" \n Or you can return an async function which will be awaited on startup. Use this option if you need to make any database queries: \n @hookimpl\ndef startup(datasette):\n async def inner():\n db = datasette.get_database()\n if \"my_table\" not in await db.table_names():\n await db.execute_write(\n \"\"\"\n create table my_table (mycol text)\n \"\"\"\n )\n\n return inner \n Potential use-cases: \n \n \n Run some initialization code for the plugin \n \n \n Create database tables that a plugin needs on startup \n \n \n Validate the metadata configuration for a plugin on startup, and raise an error if it is invalid \n \n \n \n If you are writing unit tests for a plugin that uses this hook and doesn't exercise Datasette by sending\n any simulated requests through it you will need to explicitly call await ds.invoke_startup() in your tests. An example: \n @pytest.mark.asyncio\nasync def test_my_plugin():\n ds = Datasette()\n await ds.invoke_startup()\n # Rest of test goes here \n \n Examples: datasette-saved-queries , datasette-init", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-saved-queries\", \"label\": \"datasette-saved-queries\"}, {\"href\": \"https://datasette.io/plugins/datasette-init\", \"label\": \"datasette-init\"}]"} {"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": "settings:setting-sql-time-limit-ms", "page": "settings", "ref": "setting-sql-time-limit-ms", "title": "sql_time_limit_ms", "content": "By default, queries have a time limit of one second. If a query takes longer than this to run Datasette will terminate the query and return an error. \n If this time limit is too short for you, you can customize it using the sql_time_limit_ms limit - for example, to increase it to 3.5 seconds: \n datasette mydatabase.db --setting sql_time_limit_ms 3500 \n You can optionally set a lower time limit for an individual query using the ?_timelimit=100 query string argument: \n /my-database/my-table?qSpecies=44&_timelimit=100 \n This would set the time limit to 100ms for that specific query. This feature is useful if you are working with databases of unknown size and complexity - a query that might make perfect sense for a smaller table could take too long to execute on a table with millions of rows. By setting custom time limits you can execute queries \"optimistically\" - e.g. give me an exact count of rows matching this query but only if it takes less than 100ms to calculate.", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-skip-csrf", "page": "plugin_hooks", "ref": "plugin-hook-skip-csrf", "title": "skip_csrf(datasette, scope)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n scope - dictionary \n \n The ASGI scope for the incoming HTTP request. \n \n \n \n This hook can be used to skip CSRF protection for a specific incoming request. For example, you might have a custom path at /submit-comment which is designed to accept comments from anywhere, whether or not the incoming request originated on the site and has an accompanying CSRF token. \n This example will disable CSRF protection for that specific URL path: \n from datasette import hookimpl\n\n\n@hookimpl\ndef skip_csrf(scope):\n return scope[\"path\"] == \"/submit-comment\" \n If any of the currently active skip_csrf() plugin hooks return True , CSRF protection will be skipped for the request.", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope\", \"label\": \"ASGI scope\"}]"} {"id": "plugin_hooks:plugin-hook-render-cell", "page": "plugin_hooks", "ref": "plugin-hook-render-cell", "title": "render_cell(row, value, column, table, database, datasette)", "content": "Lets you customize the display of values within table cells in the HTML table view. \n \n \n row - sqlite.Row \n \n The SQLite row object that the value being rendered is part of \n \n \n \n value - string, integer, float, bytes or None \n \n The value that was loaded from the database \n \n \n \n column - string \n \n The name of the column being rendered \n \n \n \n table - string or None \n \n The name of the table - or None if this is a custom SQL query \n \n \n \n database - string \n \n The name of the database \n \n \n \n datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n If your hook returns None , it will be ignored. Use this to indicate that your hook is not able to custom render this particular value. \n If the hook returns a string, that string will be rendered in the table cell. \n If you want to return HTML markup you can do so by returning a jinja2.Markup object. \n You can also return an awaitable function which returns a value. \n Datasette will loop through all available render_cell hooks and display the value returned by the first one that does not return None . \n Here is an example of a custom render_cell() plugin which looks for values that are a JSON string matching the following format: \n {\"href\": \"https://www.example.com/\", \"label\": \"Name\"} \n If the value matches that pattern, the plugin returns an HTML link element: \n from datasette import hookimpl\nimport markupsafe\nimport json\n\n\n@hookimpl\ndef render_cell(value):\n # Render {\"href\": \"...\", \"label\": \"...\"} as link\n if not isinstance(value, str):\n return None\n stripped = value.strip()\n if not (\n stripped.startswith(\"{\") and stripped.endswith(\"}\")\n ):\n return None\n try:\n data = json.loads(value)\n except ValueError:\n return None\n if not isinstance(data, dict):\n return None\n if set(data.keys()) != {\"href\", \"label\"}:\n return None\n href = data[\"href\"]\n if not (\n href.startswith(\"/\")\n or href.startswith(\"http://\")\n or href.startswith(\"https://\")\n ):\n return None\n return markupsafe.Markup(\n '{label}'.format(\n href=markupsafe.escape(data[\"href\"]),\n label=markupsafe.escape(data[\"label\"] or \"\")\n or \" \",\n )\n ) \n Examples: datasette-render-binary , datasette-render-markdown , datasette-json-html", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-render-binary\", \"label\": \"datasette-render-binary\"}, {\"href\": \"https://datasette.io/plugins/datasette-render-markdown\", \"label\": \"datasette-render-markdown\"}, {\"href\": \"https://datasette.io/plugins/datasette-json-html\", \"label\": \"datasette-json-html\"}]"} {"id": "plugin_hooks:plugin-register-routes", "page": "plugin_hooks", "ref": "plugin-register-routes", "title": "register_routes(datasette)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) \n \n \n \n Register additional view functions to execute for specified URL routes. \n Return a list of (regex, view_function) pairs, something like this: \n from datasette import hookimpl, Response\nimport html\n\n\nasync def hello_from(request):\n name = request.url_vars[\"name\"]\n return Response.html(\n \"Hello from {}\".format(html.escape(name))\n )\n\n\n@hookimpl\ndef register_routes():\n return [(r\"^/hello-from/(?P.*)$\", hello_from)] \n The view functions can take a number of different optional arguments. The corresponding argument will be passed to your function depending on its named parameters - a form of dependency injection. \n The optional view function arguments are as follows: \n \n \n datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n request - Request object \n \n The current HTTP request. \n \n \n \n scope - dictionary \n \n The incoming ASGI scope dictionary. \n \n \n \n send - function \n \n The ASGI send function. \n \n \n \n receive - function \n \n The ASGI receive function. \n \n \n \n The view function can be a regular function or an async def function, depending on if it needs to use any await APIs. \n The function can either return a Response class or it can return nothing and instead respond directly to the request using the ASGI send function (for advanced uses only). \n It can also raise the datasette.NotFound exception to return a 404 not found error, or the datasette.Forbidden exception for a 403 forbidden. \n See Designing URLs for your plugin for tips on designing the URL routes used by your plugin. \n Examples: datasette-auth-github , datasette-psutil", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-auth-github\", \"label\": \"datasette-auth-github\"}, {\"href\": \"https://datasette.io/plugins/datasette-psutil\", \"label\": \"datasette-psutil\"}]"} {"id": "changelog:register-routes-plugin-hooks", "page": "changelog", "ref": "register-routes-plugin-hooks", "title": "register_routes() plugin hooks", "content": "Plugins can now register new views and routes via the register_routes(datasette) plugin hook ( #819 ). View functions can be defined that accept any of the current datasette object, the current request , or the ASGI scope , send and receive objects.", "breadcrumbs": "[\"Changelog\", \"0.44 (2020-06-11)\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/819\", \"label\": \"#819\"}]"} {"id": "plugin_hooks:plugin-register-output-renderer", "page": "plugin_hooks", "ref": "plugin-register-output-renderer", "title": "register_output_renderer(datasette)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) \n \n \n \n Registers a new output renderer, to output data in a custom format. The hook function should return a dictionary, or a list of dictionaries, of the following shape: \n @hookimpl\ndef register_output_renderer(datasette):\n return {\n \"extension\": \"test\",\n \"render\": render_demo,\n \"can_render\": can_render_demo, # Optional\n } \n This will register render_demo to be called when paths with the extension .test (for example /database.test , /database/table.test , or /database/table/row.test ) are requested. \n render_demo is a Python function. It can be a regular function or an async def render_demo() awaitable function, depending on if it needs to make any asynchronous calls. \n can_render_demo is a Python function (or async def function) which accepts the same arguments as render_demo but just returns True or False . It lets Datasette know if the current SQL query can be represented by the plugin - and hence influnce if a link to this output format is displayed in the user interface. If you omit the \"can_render\" key from the dictionary every query will be treated as being supported by the plugin. \n When a request is received, the \"render\" callback function is called with zero or more of the following arguments. Datasette will inspect your callback function and pass arguments that match its function signature. \n \n \n datasette - Datasette class \n \n For accessing plugin configuration and executing queries. \n \n \n \n columns - list of strings \n \n The names of the columns returned by this query. \n \n \n \n rows - list of sqlite3.Row objects \n \n The rows returned by the query. \n \n \n \n sql - string \n \n The SQL query that was executed. \n \n \n \n query_name - string or None \n \n If this was the execution of a canned query , the name of that query. \n \n \n \n database - string \n \n The name of the database. \n \n \n \n table - string or None \n \n The table or view, if one is being rendered. \n \n \n \n request - Request object \n \n The current HTTP request. \n \n \n \n view_name - string \n \n The name of the current view being called. index , database , table , and row are the most important ones. \n \n \n \n The callback function can return None , if it is unable to render the data, or a Response class that will be returned to the caller. \n It can also return a dictionary with the following keys. This format is deprecated as-of Datasette 0.49 and will be removed by Datasette 1.0. \n \n \n body - string or bytes, optional \n \n The response body, default empty \n \n \n \n content_type - string, optional \n \n The Content-Type header, default text/plain \n \n \n \n status_code - integer, optional \n \n The HTTP status code, default 200 \n \n \n \n headers - dictionary, optional \n \n Extra HTTP headers to be returned in the response. \n \n \n \n An example of an output renderer callback function: \n def render_demo():\n return Response.text(\"Hello World\") \n Here is a more complex example: \n async def render_demo(datasette, columns, rows):\n db = datasette.get_database()\n result = await db.execute(\"select sqlite_version()\")\n first_row = \" | \".join(columns)\n lines = [first_row]\n lines.append(\"=\" * len(first_row))\n for row in rows:\n lines.append(\" | \".join(row))\n return Response(\n \"\\n\".join(lines),\n content_type=\"text/plain; charset=utf-8\",\n headers={\"x-sqlite-version\": result.first()[0]},\n ) \n And here is an example can_render function which returns True only if the query results contain the columns atom_id , atom_title and atom_updated : \n def can_render_demo(columns):\n return {\n \"atom_id\",\n \"atom_title\",\n \"atom_updated\",\n }.issubset(columns) \n Examples: datasette-atom , datasette-ics , datasette-geojson , datasette-copyable", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-atom\", \"label\": \"datasette-atom\"}, {\"href\": \"https://datasette.io/plugins/datasette-ics\", \"label\": \"datasette-ics\"}, {\"href\": \"https://datasette.io/plugins/datasette-geojson\", \"label\": \"datasette-geojson\"}, {\"href\": \"https://datasette.io/plugins/datasette-copyable\", \"label\": \"datasette-copyable\"}]"} {"id": "changelog:v0-28-register-output-renderer", "page": "changelog", "ref": "v0-28-register-output-renderer", "title": "register_output_renderer plugins", "content": "Russ Garrett implemented a new Datasette plugin hook called register_output_renderer ( #441 ) which allows plugins to create additional output renderers in addition to Datasette's default .json and .csv . \n Russ's in-development datasette-geo plugin includes an example of this hook being used to output .geojson automatically converted from SpatiaLite.", "breadcrumbs": "[\"Changelog\", \"0.28 (2019-05-19)\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/pull/441\", \"label\": \"#441\"}, {\"href\": \"https://github.com/russss/datasette-geo\", \"label\": \"datasette-geo\"}, {\"href\": \"https://github.com/russss/datasette-geo/blob/d4cecc020848bbde91e9e17bf352f7c70bc3dccf/datasette_plugin_geo/geojson.py\", \"label\": \"an example\"}]"} {"id": "plugin_hooks:plugin-hook-register-magic-parameters", "page": "plugin_hooks", "ref": "plugin-hook-register-magic-parameters", "title": "register_magic_parameters(datasette)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) . \n \n \n \n Magic parameters can be used to add automatic parameters to canned queries . This plugin hook allows additional magic parameters to be defined by plugins. \n Magic parameters all take this format: _prefix_rest_of_parameter . The prefix indicates which magic parameter function should be called - the rest of the parameter is passed as an argument to that function. \n To register a new function, return it as a tuple of (string prefix, function) from this hook. The function you register should take two arguments: key and request , where key is the rest_of_parameter portion of the parameter and request is the current Request object . \n This example registers two new magic parameters: :_request_http_version returning the HTTP version of the current request, and :_uuid_new which returns a new UUID: \n from uuid import uuid4\n\n\ndef uuid(key, request):\n if key == \"new\":\n return str(uuid4())\n else:\n raise KeyError\n\n\ndef request(key, request):\n if key == \"http_version\":\n return request.scope[\"http_version\"]\n else:\n raise KeyError\n\n\n@hookimpl\ndef register_magic_parameters(datasette):\n return [\n (\"request\", request),\n (\"uuid\", uuid),\n ]", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[]"} {"id": "plugin_hooks:plugin-register-facet-classes", "page": "plugin_hooks", "ref": "plugin-register-facet-classes", "title": "register_facet_classes()", "content": "Return a list of additional Facet subclasses to be registered. \n \n The design of this plugin hook is unstable and may change. See issue 830 . \n \n Each Facet subclass implements a new type of facet operation. The class should look like this: \n class SpecialFacet(Facet):\n # This key must be unique across all facet classes:\n type = \"special\"\n\n async def suggest(self):\n # Use self.sql and self.params to suggest some facets\n suggested_facets = []\n suggested_facets.append(\n {\n \"name\": column, # Or other unique name\n # Construct the URL that will enable this facet:\n \"toggle_url\": self.ds.absolute_url(\n self.request,\n path_with_added_args(\n self.request, {\"_facet\": column}\n ),\n ),\n }\n )\n return suggested_facets\n\n async def facet_results(self):\n # This should execute the facet operation and return results, again\n # using self.sql and self.params as the starting point\n facet_results = []\n facets_timed_out = []\n facet_size = self.get_facet_size()\n # Do some calculations here...\n for column in columns_selected_for_facet:\n try:\n facet_results_values = []\n # More calculations...\n facet_results_values.append(\n {\n \"value\": value,\n \"label\": label,\n \"count\": count,\n \"toggle_url\": self.ds.absolute_url(\n self.request, toggle_path\n ),\n \"selected\": selected,\n }\n )\n facet_results.append(\n {\n \"name\": column,\n \"results\": facet_results_values,\n \"truncated\": len(facet_rows_results)\n > facet_size,\n }\n )\n except QueryInterrupted:\n facets_timed_out.append(column)\n\n return facet_results, facets_timed_out \n See datasette/facets.py for examples of how these classes can work. \n The plugin hook can then be used to register the new facet class like this: \n @hookimpl\ndef register_facet_classes():\n return [SpecialFacet]", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/830\", \"label\": \"issue 830\"}, {\"href\": \"https://github.com/simonw/datasette/blob/main/datasette/facets.py\", \"label\": \"datasette/facets.py\"}]"} {"id": "plugin_hooks:plugin-hook-register-commands", "page": "plugin_hooks", "ref": "plugin-hook-register-commands", "title": "register_commands(cli)", "content": "cli - the root Datasette Click command group \n \n Use this to register additional CLI commands \n \n \n \n Register additional CLI commands that can be run using datsette yourcommand ... . This provides a mechanism by which plugins can add new CLI commands to Datasette. \n This example registers a new datasette verify file1.db file2.db command that checks if the provided file paths are valid SQLite databases: \n from datasette import hookimpl\nimport click\nimport sqlite3\n\n\n@hookimpl\ndef register_commands(cli):\n @cli.command()\n @click.argument(\n \"files\", type=click.Path(exists=True), nargs=-1\n )\n def verify(files):\n \"Verify that files can be opened by Datasette\"\n for file in files:\n conn = sqlite3.connect(str(file))\n try:\n conn.execute(\"select * from sqlite_master\")\n except sqlite3.DatabaseError:\n raise click.ClickException(\n \"Invalid database: {}\".format(file)\n ) \n The new command can then be executed like so: \n datasette verify fixtures.db \n Help text (from the docstring for the function plus any defined Click arguments or options) will become available using: \n datasette verify --help \n Plugins can register multiple commands by making multiple calls to the @cli.command() decorator. Consult the Click documentation for full details on how to build a CLI command, including how to define arguments and options. \n Note that register_commands() plugins cannot used with the --plugins-dir mechanism - they need to be installed into the same virtual environment as Datasette using pip install . Provided it has a setup.py file (see Packaging a plugin ) you can run pip install directly against the directory in which you are developing your plugin like so: \n pip install -e path/to/my/datasette-plugin \n Examples: datasette-auth-passwords , datasette-verify", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://click.palletsprojects.com/en/latest/commands/#callback-invocation\", \"label\": \"Click command group\"}, {\"href\": \"https://click.palletsprojects.com/\", \"label\": \"Click documentation\"}, {\"href\": \"https://datasette.io/plugins/datasette-auth-passwords\", \"label\": \"datasette-auth-passwords\"}, {\"href\": \"https://datasette.io/plugins/datasette-verify\", \"label\": \"datasette-verify\"}]"} {"id": "plugin_hooks:plugin-hook-publish-subcommand", "page": "plugin_hooks", "ref": "plugin-hook-publish-subcommand", "title": "publish_subcommand(publish)", "content": "publish - Click publish command group \n \n The Click command group for the datasette publish subcommand \n \n \n \n This hook allows you to create new providers for the datasette publish \n command. Datasette uses this hook internally to implement the default cloudrun \n and heroku subcommands, so you can read\n their source \n to see examples of this hook in action. \n Let's say you want to build a plugin that adds a datasette publish my_hosting_provider --api_key=xxx mydatabase.db publish command. Your implementation would start like this: \n from datasette import hookimpl\nfrom datasette.publish.common import (\n add_common_publish_arguments_and_options,\n)\nimport click\n\n\n@hookimpl\ndef publish_subcommand(publish):\n @publish.command()\n @add_common_publish_arguments_and_options\n @click.option(\n \"-k\",\n \"--api_key\",\n help=\"API key for talking to my hosting provider\",\n )\n def my_hosting_provider(\n files,\n metadata,\n extra_options,\n branch,\n template_dir,\n plugins_dir,\n static,\n install,\n plugin_secret,\n version_note,\n secret,\n title,\n license,\n license_url,\n source,\n source_url,\n about,\n about_url,\n api_key,\n ):\n ... \n Examples: datasette-publish-fly , datasette-publish-vercel", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/tree/main/datasette/publish\", \"label\": \"their source\"}, {\"href\": \"https://datasette.io/plugins/datasette-publish-fly\", \"label\": \"datasette-publish-fly\"}, {\"href\": \"https://datasette.io/plugins/datasette-publish-vercel\", \"label\": \"datasette-publish-vercel\"}]"} {"id": "plugin_hooks:plugin-hook-prepare-jinja2-environment", "page": "plugin_hooks", "ref": "plugin-hook-prepare-jinja2-environment", "title": "prepare_jinja2_environment(env, datasette)", "content": "env - jinja2 Environment \n \n The template environment that is being prepared \n \n \n \n datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) \n \n \n \n This hook is called with the Jinja2 environment that is used to evaluate\n Datasette HTML templates. You can use it to do things like register custom\n template filters , for\n example: \n from datasette import hookimpl\n\n\n@hookimpl\ndef prepare_jinja2_environment(env):\n env.filters[\"uppercase\"] = lambda u: u.upper() \n You can now use this filter in your custom templates like so: \n Table name: {{ table|uppercase }} \n This function can return an awaitable function if it needs to run any async code. \n Examples: datasette-edit-templates", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"http://jinja.pocoo.org/docs/2.10/api/#custom-filters\", \"label\": \"register custom\\n template filters\"}, {\"href\": \"https://datasette.io/plugins/datasette-edit-templates\", \"label\": \"datasette-edit-templates\"}]"} {"id": "plugin_hooks:plugin-hook-prepare-connection", "page": "plugin_hooks", "ref": "plugin-hook-prepare-connection", "title": "prepare_connection(conn, database, datasette)", "content": "conn - sqlite3 connection object \n \n The connection that is being opened \n \n \n \n database - string \n \n The name of the database \n \n \n \n datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) \n \n \n \n This hook is called when a new SQLite database connection is created. You can\n use it to register custom SQL functions ,\n aggregates and collations. For example: \n from datasette import hookimpl\nimport random\n\n\n@hookimpl\ndef prepare_connection(conn):\n conn.create_function(\n \"random_integer\", 2, random.randint\n ) \n This registers a SQL function called random_integer which takes two\n arguments and can be called like this: \n select random_integer(1, 10); \n Examples: datasette-jellyfish , datasette-jq , datasette-haversine , datasette-rure", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://docs.python.org/2/library/sqlite3.html#sqlite3.Connection.create_function\", \"label\": \"register custom SQL functions\"}, {\"href\": \"https://datasette.io/plugins/datasette-jellyfish\", \"label\": \"datasette-jellyfish\"}, {\"href\": \"https://datasette.io/plugins/datasette-jq\", \"label\": \"datasette-jq\"}, {\"href\": \"https://datasette.io/plugins/datasette-haversine\", \"label\": \"datasette-haversine\"}, {\"href\": \"https://datasette.io/plugins/datasette-rure\", \"label\": \"datasette-rure\"}]"} {"id": "authentication:permissions-permissions-debug", "page": "authentication", "ref": "permissions-permissions-debug", "title": "permissions-debug", "content": "Actor is allowed to view the /-/permissions debug page. \n Default deny .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-permission-allowed", "page": "plugin_hooks", "ref": "plugin-hook-permission-allowed", "title": "permission_allowed(datasette, actor, action, resource)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n actor - dictionary \n \n The current actor, as decided by actor_from_request(datasette, request) . \n \n \n \n action - string \n \n The action to be performed, e.g. \"edit-table\" . \n \n \n \n resource - string or None \n \n An identifier for the individual resource, e.g. the name of the table. \n \n \n \n Called to check that an actor has permission to perform an action on a resource. Can return True if the action is allowed, False if the action is not allowed or None if the plugin does not have an opinion one way or the other. \n Here's an example plugin which randomly selects if a permission should be allowed or denied, except for view-instance which always uses the default permission scheme instead. \n from datasette import hookimpl\nimport random\n\n\n@hookimpl\ndef permission_allowed(action):\n if action != \"view-instance\":\n # Return True or False at random\n return random.random() > 0.5\n # Returning None falls back to default permissions \n This function can alternatively return an awaitable function which itself returns True , False or None . You can use this option if you need to execute additional database queries using await datasette.execute(...) . \n Here's an example that allows users to view the admin_log table only if their actor id is present in the admin_users table. It aso disallows arbitrary SQL queries for the staff.db database for all users. \n @hookimpl\ndef permission_allowed(datasette, actor, action, resource):\n async def inner():\n if action == \"execute-sql\" and resource == \"staff\":\n return False\n if action == \"view-table\" and resource == (\n \"staff\",\n \"admin_log\",\n ):\n if not actor:\n return False\n user_id = actor[\"id\"]\n return await datasette.get_database(\n \"staff\"\n ).execute(\n \"select count(*) from admin_users where user_id = :user_id\",\n {\"user_id\": user_id},\n )\n\n return inner \n See built-in permissions for a full list of permissions that are included in Datasette core. \n Example: datasette-permissions-sql", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-permissions-sql\", \"label\": \"datasette-permissions-sql\"}]"} {"id": "internals:internals-utils-parse-metadata", "page": "internals", "ref": "internals-utils-parse-metadata", "title": "parse_metadata(content)", "content": "This function accepts a string containing either JSON or YAML, expected to be of the format described in Metadata . It returns a nested Python dictionary representing the parsed data from that string. \n If the metadata cannot be parsed as either JSON or YAML the function will raise a utils.BadMetadataError exception. \n \n \n datasette.utils. parse_metadata content : str dict \n \n Detects if content is JSON or YAML and parses it appropriately.", "breadcrumbs": "[\"Internals for plugins\", \"The datasette.utils module\"]", "references": "[]"} {"id": "settings:setting-num-sql-threads", "page": "settings", "ref": "setting-num-sql-threads", "title": "num_sql_threads", "content": "Maximum number of threads in the thread pool Datasette uses to execute SQLite queries. Defaults to 3. \n datasette mydatabase.db --setting num_sql_threads 10 \n Setting this to 0 turns off threaded SQL queries entirely - useful for environments that do not support threading such as Pyodide .", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[{\"href\": \"https://pyodide.org/\", \"label\": \"Pyodide\"}]"} {"id": "plugin_hooks:plugin-hook-menu-links", "page": "plugin_hooks", "ref": "plugin-hook-menu-links", "title": "menu_links(datasette, actor, request)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n actor - dictionary or None \n \n The currently authenticated actor . \n \n \n \n request - Request object or None \n \n The current HTTP request. This can be None if the request object is not available. \n \n \n \n This hook allows additional items to be included in the menu displayed by Datasette's top right menu icon. \n The hook should return a list of {\"href\": \"...\", \"label\": \"...\"} menu items. These will be added to the menu. \n It can alternatively return an async def awaitable function which returns a list of menu items. \n This example adds a new menu item but only if the signed in user is \"root\" : \n from datasette import hookimpl\n\n\n@hookimpl\ndef menu_links(datasette, actor):\n if actor and actor.get(\"id\") == \"root\":\n return [\n {\n \"href\": datasette.urls.path(\n \"/-/edit-schema\"\n ),\n \"label\": \"Edit schema\",\n },\n ] \n Using datasette.urls here ensures that links in the menu will take the base_url setting into account. \n Examples: datasette-search-all , datasette-graphql", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-search-all\", \"label\": \"datasette-search-all\"}, {\"href\": \"https://datasette.io/plugins/datasette-graphql\", \"label\": \"datasette-graphql\"}]"} {"id": "settings:setting-max-returned-rows", "page": "settings", "ref": "setting-max-returned-rows", "title": "max_returned_rows", "content": "Datasette returns a maximum of 1,000 rows of data at a time. If you execute a query that returns more than 1,000 rows, Datasette will return the first 1,000 and include a warning that the result set has been truncated. You can use OFFSET/LIMIT or other methods in your SQL to implement pagination if you need to return more than 1,000 rows. \n You can increase or decrease this limit like so: \n datasette mydatabase.db --setting max_returned_rows 2000", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-max-csv-mb", "page": "settings", "ref": "setting-max-csv-mb", "title": "max_csv_mb", "content": "The maximum size of CSV that can be exported, in megabytes. Defaults to 100MB.\n You can disable the limit entirely by settings this to 0: \n datasette mydatabase.db --setting max_csv_mb 0", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "changelog:latest-datasette-io", "page": "changelog", "ref": "latest-datasette-io", "title": "latest.datasette.io", "content": "Every commit to Datasette master is now automatically deployed by Travis CI to\n https://latest.datasette.io/ - ensuring there is always a live demo of the\n latest version of the software. \n The demo uses the fixtures from our\n unit tests, ensuring it demonstrates the same range of functionality that is\n covered by the tests. \n You can see how the deployment mechanism works in our .travis.yml file.", "breadcrumbs": "[\"Changelog\", \"0.23 (2018-06-18)\"]", "references": "[{\"href\": \"https://latest.datasette.io/\", \"label\": \"https://latest.datasette.io/\"}, {\"href\": \"https://github.com/simonw/datasette/blob/master/tests/fixtures.py\", \"label\": \"the fixtures\"}, {\"href\": \"https://github.com/simonw/datasette/blob/master/.travis.yml\", \"label\": \".travis.yml\"}]"} {"id": "sql_queries:hide-sql", "page": "sql_queries", "ref": "hide-sql", "title": "hide_sql", "content": "Canned queries default to displaying their SQL query at the top of the page. If the query is extremely long you may want to hide it by default, with a \"show\" link that can be used to make it visible. \n Add the \"hide_sql\": true option to hide the SQL query by default.", "breadcrumbs": "[\"Running SQL queries\", \"Canned queries\", \"Additional canned query options\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-handle-exception", "page": "plugin_hooks", "ref": "plugin-hook-handle-exception", "title": "handle_exception(datasette, request, exception)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to render templates or execute SQL queries. \n \n \n \n request - Request object \n \n The current HTTP request. \n \n \n \n exception - Exception \n \n The exception that was raised. \n \n \n \n This hook is called any time an unexpected exception is raised. You can use it to record the exception. \n If your handler returns a Response object it will be returned to the client in place of the default Datasette error page. \n The handler can return a response directly, or it can return return an awaitable function that returns a response. \n This example logs an error to Sentry and then renders a custom error page: \n from datasette import hookimpl, Response\nimport sentry_sdk\n\n\n@hookimpl\ndef handle_exception(datasette, exception):\n sentry_sdk.capture_exception(exception)\n\n async def inner():\n return Response.html(\n await datasette.render_template(\n \"custom_error.html\", request=request\n )\n )\n\n return inner \n Example: datasette-sentry", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://sentry.io/\", \"label\": \"Sentry\"}, {\"href\": \"https://datasette.io/plugins/datasette-sentry\", \"label\": \"datasette-sentry\"}]"} {"id": "plugin_hooks:plugin-hook-get-metadata", "page": "plugin_hooks", "ref": "plugin-hook-get-metadata", "title": "get_metadata(datasette, key, database, table)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) . \n \n \n \n actor - dictionary or None \n \n The currently authenticated actor . \n \n \n \n database - string or None \n \n The name of the database metadata is being asked for. \n \n \n \n table - string or None \n \n The name of the table. \n \n \n \n key - string or None \n \n The name of the key for which data is being asked for. \n \n \n \n This hook is responsible for returning a dictionary corresponding to Datasette Metadata . This function is passed the database , table and key which were passed to the upstream internal request for metadata. Regardless, it is important to return a global metadata object, where \"databases\": [] would be a top-level key. The dictionary returned here, will be merged with, and overwritten by, the contents of the physical metadata.yaml if one is present. \n \n The design of this plugin hook does not currently provide a mechanism for interacting with async code, and may change in the future. See issue 1384 . \n \n @hookimpl\ndef get_metadata(datasette, key, database, table):\n metadata = {\n \"title\": \"This will be the Datasette landing page title!\",\n \"description\": get_instance_description(datasette),\n \"databases\": [],\n }\n for db_name, db_data_dict in get_my_database_meta(\n datasette, database, table, key\n ):\n metadata[\"databases\"][db_name] = db_data_dict\n # whatever we return here will be merged with any other plugins using this hook and\n # will be overwritten by a local metadata.yaml if one exists!\n return metadata \n Example: datasette-remote-metadata plugin", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/1384\", \"label\": \"issue 1384\"}, {\"href\": \"https://datasette.io/plugins/datasette-remote-metadata\", \"label\": \"datasette-remote-metadata plugin\"}]"} {"id": "sql_queries:fragment", "page": "sql_queries", "ref": "fragment", "title": "fragment", "content": "Some plugins, such as datasette-vega , can be configured by including additional data in the fragment hash of the URL - the bit that comes after a # symbol. \n You can set a default fragment hash that will be included in the link to the canned query from the database index page using the \"fragment\" key. \n This example demonstrates both fragment and hide_sql : \n {\n \"databases\": {\n \"fixtures\": {\n \"queries\": {\n \"neighborhood_search\": {\n \"sql\": \"select neighborhood, facet_cities.name, state\\nfrom facetable join facet_cities on facetable.city_id = facet_cities.id\\nwhere neighborhood like '%' || :text || '%' order by neighborhood;\",\n \"fragment\": \"fragment-goes-here\",\n \"hide_sql\": true\n }\n }\n }\n }\n} \n See here for a demo of this in action.", "breadcrumbs": "[\"Running SQL queries\", \"Canned queries\", \"Additional canned query options\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette-vega\", \"label\": \"datasette-vega\"}, {\"href\": \"https://latest.datasette.io/fixtures#queries\", \"label\": \"See here\"}]"} {"id": "settings:setting-force-https-urls", "page": "settings", "ref": "setting-force-https-urls", "title": "force_https_urls", "content": "Forces self-referential URLs in the JSON output to always use the https:// \n protocol. This is useful for cases where the application itself is hosted using\n HTTP but is served to the outside world via a proxy that enables HTTPS. \n datasette mydatabase.db --setting force_https_urls 1", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-forbidden", "page": "plugin_hooks", "ref": "plugin-hook-forbidden", "title": "forbidden(datasette, request, message)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to render templates or execute SQL queries. \n \n \n \n request - Request object \n \n The current HTTP request. \n \n \n \n message - string \n \n A message hinting at why the request was forbidden. \n \n \n \n Plugins can use this to customize how Datasette responds when a 403 Forbidden error occurs - usually because a page failed a permission check, see Permissions . \n If a plugin hook wishes to react to the error, it should return a Response object . \n This example returns a redirect to a /-/login page: \n from datasette import hookimpl\nfrom urllib.parse import urlencode\n\n\n@hookimpl\ndef forbidden(request, message):\n return Response.redirect(\n \"/-/login?=\" + urlencode({\"message\": message})\n ) \n The function can alternatively return an awaitable function if it needs to make any asynchronous method calls. This example renders a template: \n from datasette import hookimpl, Response\n\n\n@hookimpl\ndef forbidden(datasette):\n async def inner():\n return Response.html(\n await datasette.render_template(\n \"render_message.html\", request=request\n )\n )\n\n return inner", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-filters-from-request", "page": "plugin_hooks", "ref": "plugin-hook-filters-from-request", "title": "filters_from_request(request, database, table, datasette)", "content": "request - Request object \n \n The current HTTP request. \n \n \n \n database - string \n \n The name of the database. \n \n \n \n table - string \n \n The name of the table. \n \n \n \n datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n This hook runs on the table page, and can influence the where clause of the SQL query used to populate that page, based on query string arguments on the incoming request. \n The hook should return an instance of datasette.filters.FilterArguments which has one required and three optional arguments: \n return FilterArguments(\n where_clauses=[\"id > :max_id\"],\n params={\"max_id\": 5},\n human_descriptions=[\"max_id is greater than 5\"],\n extra_context={},\n) \n The arguments to the FilterArguments class constructor are as follows: \n \n \n where_clauses - list of strings, required \n \n A list of SQL fragments that will be inserted into the SQL query, joined by the and operator. These can include :named parameters which will be populated using data in params . \n \n \n \n params - dictionary, optional \n \n Additional keyword arguments to be used when the query is executed. These should match any :arguments in the where clauses. \n \n \n \n human_descriptions - list of strings, optional \n \n These strings will be included in the human-readable description at the top of the page and the page . \n \n \n \n extra_context - dictionary, optional \n \n Additional context variables that should be made available to the table.html template when it is rendered. \n \n \n \n This example plugin causes 0 results to be returned if ?_nothing=1 is added to the URL: \n from datasette import hookimpl\nfrom datasette.filters import FilterArguments\n\n\n@hookimpl\ndef filters_from_request(self, request):\n if request.args.get(\"_nothing\"):\n return FilterArguments(\n [\"1 = 0\"], human_descriptions=[\"NOTHING\"]\n ) \n Example: datasette-leaflet-freedraw", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-leaflet-freedraw\", \"label\": \"datasette-leaflet-freedraw\"}]"} {"id": "settings:setting-facet-time-limit-ms", "page": "settings", "ref": "setting-facet-time-limit-ms", "title": "facet_time_limit_ms", "content": "This is the time limit Datasette allows for calculating a facet, which defaults to 200ms: \n datasette mydatabase.db --setting facet_time_limit_ms 1000", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-facet-suggest-time-limit-ms", "page": "settings", "ref": "setting-facet-suggest-time-limit-ms", "title": "facet_suggest_time_limit_ms", "content": "When Datasette calculates suggested facets it needs to run a SQL query for every column in your table. The default for this time limit is 50ms to account for the fact that it needs to run once for every column. If the time limit is exceeded the column will not be suggested as a facet. \n You can increase this time limit like so: \n datasette mydatabase.db --setting facet_suggest_time_limit_ms 500", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-extra-template-vars", "page": "plugin_hooks", "ref": "plugin-hook-extra-template-vars", "title": "extra_template_vars(template, database, table, columns, view_name, request, datasette)", "content": "Extra template variables that should be made available in the rendered template context. \n \n \n template - string \n \n The template that is being rendered, e.g. database.html \n \n \n \n database - string or None \n \n The name of the database, or None if the page does not correspond to a database (e.g. the root page) \n \n \n \n table - string or None \n \n The name of the table, or None if the page does not correct to a table \n \n \n \n columns - list of strings or None \n \n The names of the database columns that will be displayed on this page. None if the page does not contain a table. \n \n \n \n view_name - string \n \n The name of the view being displayed. ( index , database , table , and row are the most important ones.) \n \n \n \n request - Request object or None \n \n The current HTTP request. This can be None if the request object is not available. \n \n \n \n datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) \n \n \n \n This hook can return one of three different types: \n \n \n Dictionary \n \n If you return a dictionary its keys and values will be merged into the template context. \n \n \n \n Function that returns a dictionary \n \n If you return a function it will be executed. If it returns a dictionary those values will will be merged into the template context. \n \n \n \n Function that returns an awaitable function that returns a dictionary \n \n You can also return a function which returns an awaitable function which returns a dictionary. \n \n \n \n Datasette runs Jinja2 in async mode , which means you can add awaitable functions to the template scope and they will be automatically awaited when they are rendered by the template. \n Here's an example plugin that adds a \"user_agent\" variable to the template context containing the current request's User-Agent header: \n @hookimpl\ndef extra_template_vars(request):\n return {\"user_agent\": request.headers.get(\"user-agent\")} \n This example returns an awaitable function which adds a list of hidden_table_names to the context: \n @hookimpl\ndef extra_template_vars(datasette, database):\n async def hidden_table_names():\n if database:\n db = datasette.databases[database]\n return {\n \"hidden_table_names\": await db.hidden_table_names()\n }\n else:\n return {}\n\n return hidden_table_names \n And here's an example which adds a sql_first(sql_query) function which executes a SQL statement and returns the first column of the first row of results: \n @hookimpl\ndef extra_template_vars(datasette, database):\n async def sql_first(sql, dbname=None):\n dbname = (\n dbname\n or database\n or next(iter(datasette.databases.keys()))\n )\n result = await datasette.execute(dbname, sql)\n return result.rows[0][0]\n\n return {\"sql_first\": sql_first} \n You can then use the new function in a template like so: \n SQLite version: {{ sql_first(\"select sqlite_version()\") }} \n Examples: datasette-search-all , datasette-template-sql", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://jinja.palletsprojects.com/en/2.10.x/api/#async-support\", \"label\": \"async mode\"}, {\"href\": \"https://datasette.io/plugins/datasette-search-all\", \"label\": \"datasette-search-all\"}, {\"href\": \"https://datasette.io/plugins/datasette-template-sql\", \"label\": \"datasette-template-sql\"}]"} {"id": "plugin_hooks:plugin-hook-extra-js-urls", "page": "plugin_hooks", "ref": "plugin-hook-extra-js-urls", "title": "extra_js_urls(template, database, table, columns, view_name, request, datasette)", "content": "This takes the same arguments as extra_template_vars(...) \n This works in the same way as extra_css_urls() but for JavaScript. You can\n return a list of URLs, a list of dictionaries or an awaitable function that returns those things: \n from datasette import hookimpl\n\n\n@hookimpl\ndef extra_js_urls():\n return [\n {\n \"url\": \"https://code.jquery.com/jquery-3.3.1.slim.min.js\",\n \"sri\": \"sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo\",\n }\n ] \n You can also return URLs to files from your plugin's static/ directory, if\n you have one: \n @hookimpl\ndef extra_js_urls():\n return [\"/-/static-plugins/your-plugin/app.js\"] \n Note that your-plugin here should be the hyphenated plugin name - the name that is displayed in the list on the /-/plugins debug page. \n If your code uses JavaScript modules you should include the \"module\": True key. See Custom CSS and JavaScript for more details. \n @hookimpl\ndef extra_js_urls():\n return [\n {\n \"url\": \"/-/static-plugins/your-plugin/app.js\",\n \"module\": True,\n }\n ] \n Examples: datasette-cluster-map , datasette-vega", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules\", \"label\": \"JavaScript modules\"}, {\"href\": \"https://datasette.io/plugins/datasette-cluster-map\", \"label\": \"datasette-cluster-map\"}, {\"href\": \"https://datasette.io/plugins/datasette-vega\", \"label\": \"datasette-vega\"}]"} {"id": "plugin_hooks:plugin-hook-extra-css-urls", "page": "plugin_hooks", "ref": "plugin-hook-extra-css-urls", "title": "extra_css_urls(template, database, table, columns, view_name, request, datasette)", "content": "This takes the same arguments as extra_template_vars(...) \n Return a list of extra CSS URLs that should be included on the page. These can\n take advantage of the CSS class hooks described in Custom pages and templates . \n This can be a list of URLs: \n from datasette import hookimpl\n\n\n@hookimpl\ndef extra_css_urls():\n return [\n \"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css\"\n ] \n Or a list of dictionaries defining both a URL and an\n SRI hash : \n @hookimpl\ndef extra_css_urls():\n return [\n {\n \"url\": \"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css\",\n \"sri\": \"sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4\",\n }\n ] \n This function can also return an awaitable function, useful if it needs to run any async code: \n @hookimpl\ndef extra_css_urls(datasette):\n async def inner():\n db = datasette.get_database()\n results = await db.execute(\n \"select url from css_files\"\n )\n return [r[0] for r in results]\n\n return inner \n Examples: datasette-cluster-map , datasette-vega", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://www.srihash.org/\", \"label\": \"SRI hash\"}, {\"href\": \"https://datasette.io/plugins/datasette-cluster-map\", \"label\": \"datasette-cluster-map\"}, {\"href\": \"https://datasette.io/plugins/datasette-vega\", \"label\": \"datasette-vega\"}]"} {"id": "plugin_hooks:plugin-hook-extra-body-script", "page": "plugin_hooks", "ref": "plugin-hook-extra-body-script", "title": "extra_body_script(template, database, table, columns, view_name, request, datasette)", "content": "Extra JavaScript to be added to a <script> block at the end of the <body> element on the page. \n This takes the same arguments as extra_template_vars(...) \n The template , database , table and view_name options can be used to return different code depending on which template is being rendered and which database or table are being processed. \n The datasette instance is provided primarily so that you can consult any plugin configuration options that may have been set, using the datasette.plugin_config(plugin_name) method documented above. \n This function can return a string containing JavaScript, or a dictionary as described below, or a function or awaitable function that returns a string or dictionary. \n Use a dictionary if you want to specify that the code should be placed in a <script type=\"module\">...</script> element: \n @hookimpl\ndef extra_body_script():\n return {\n \"module\": True,\n \"script\": \"console.log('Your JavaScript goes here...')\",\n } \n This will add the following to the end of your page: \n <script type=\"module\">console.log('Your JavaScript goes here...')</script> \n Example: datasette-cluster-map", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-cluster-map\", \"label\": \"datasette-cluster-map\"}]"} {"id": "authentication:permissions-execute-sql", "page": "authentication", "ref": "permissions-execute-sql", "title": "execute-sql", "content": "Actor is allowed to run arbitrary SQL queries against a specific database, e.g. https://latest.datasette.io/fixtures?sql=select+100 \n \n \n resource - string \n \n The name of the database \n \n \n \n Default allow . See also the default_allow_sql setting .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures?sql=select+100\", \"label\": \"https://latest.datasette.io/fixtures?sql=select+100\"}]"} {"id": "settings:setting-default-page-size", "page": "settings", "ref": "setting-default-page-size", "title": "default_page_size", "content": "The default number of rows returned by the table page. You can over-ride this on a per-page basis using the ?_size=80 query string parameter, provided you do not specify a value higher than the max_returned_rows setting. You can set this default using --setting like so: \n datasette mydatabase.db --setting default_page_size 50", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-default-facet-size", "page": "settings", "ref": "setting-default-facet-size", "title": "default_facet_size", "content": "The default number of unique rows returned by Facets is 30. You can customize it like this: \n datasette mydatabase.db --setting default_facet_size 50", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-default-cache-ttl", "page": "settings", "ref": "setting-default-cache-ttl", "title": "default_cache_ttl", "content": "Default HTTP caching max-age header in seconds, used for Cache-Control: max-age=X . Can be over-ridden on a per-request basis using the ?_ttl= query string parameter. Set this to 0 to disable HTTP caching entirely. Defaults to 5 seconds. \n datasette mydatabase.db --setting default_cache_ttl 60", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-default-allow-sql", "page": "settings", "ref": "setting-default-allow-sql", "title": "default_allow_sql", "content": "Should users be able to execute arbitrary SQL queries by default? \n Setting this to off causes permission checks for execute-sql to fail by default. \n datasette mydatabase.db --setting default_allow_sql off \n There are two ways to achieve this: the other is to add \"allow_sql\": false to your metadata.json file, as described in Controlling the ability to execute arbitrary SQL . This setting offers a more convenient way to do this.", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "authentication:permissions-debug-menu", "page": "authentication", "ref": "permissions-debug-menu", "title": "debug-menu", "content": "Controls if the various debug pages are displayed in the navigation menu. \n Default deny .", "breadcrumbs": "[\"Authentication and permissions\", \"Built-in permissions\"]", "references": "[]"} {"id": "internals:database-hash", "page": "internals", "ref": "database-hash", "title": "db.hash", "content": "If the database was opened in immutable mode, this property returns the 64 character SHA-256 hash of the database contents as a string. Otherwise it returns None .", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[]"} {"id": "internals:database-close", "page": "internals", "ref": "database-close", "title": "db.close()", "content": "Closes all of the open connections to file-backed databases. This is mainly intended to be used by large test suites, to avoid hitting limits on the number of open files.", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[]"} {"id": "internals:internals-datasette-urls", "page": "internals", "ref": "internals-datasette-urls", "title": "datasette.urls", "content": "The datasette.urls object contains methods for building URLs to pages within Datasette. Plugins should use this to link to pages, since these methods take into account any base_url configuration setting that might be in effect. \n \n \n datasette.urls.instance(format=None) \n \n Returns the URL to the Datasette instance root page. This is usually \"/\" . \n \n \n \n datasette.urls.path(path, format=None) \n \n Takes a path and returns the full path, taking base_url into account. \n For example, datasette.urls.path(\"-/logout\") will return the path to the logout page, which will be \"/-/logout\" by default or /prefix-path/-/logout if base_url is set to /prefix-path/ \n \n \n \n datasette.urls.logout() \n \n Returns the URL to the logout page, usually \"/-/logout\" \n \n \n \n datasette.urls.static(path) \n \n Returns the URL of one of Datasette's default static assets, for example \"/-/static/app.css\" \n \n \n \n datasette.urls.static_plugins(plugin_name, path) \n \n Returns the URL of one of the static assets belonging to a plugin. \n datasette.urls.static_plugins(\"datasette_cluster_map\", \"datasette-cluster-map.js\") would return \"/-/static-plugins/datasette_cluster_map/datasette-cluster-map.js\" \n \n \n \n datasette.urls.static(path) \n \n Returns the URL of one of Datasette's default static assets, for example \"/-/static/app.css\" \n \n \n \n datasette.urls.database(database_name, format=None) \n \n Returns the URL to a database page, for example \"/fixtures\" \n \n \n \n datasette.urls.table(database_name, table_name, format=None) \n \n Returns the URL to a table page, for example \"/fixtures/facetable\" \n \n \n \n datasette.urls.query(database_name, query_name, format=None) \n \n Returns the URL to a query page, for example \"/fixtures/pragma_cache_size\" \n \n \n \n These functions can be accessed via the {{ urls }} object in Datasette templates, for example: \n <a href=\"{{ urls.instance() }}\">Homepage</a>\n<a href=\"{{ urls.database(\"fixtures\") }}\">Fixtures database</a>\n<a href=\"{{ urls.table(\"fixtures\", \"facetable\") }}\">facetable table</a>\n<a href=\"{{ urls.query(\"fixtures\", \"pragma_cache_size\") }}\">pragma_cache_size query</a> \n Use the format=\"json\" (or \"csv\" or other formats supported by plugins) arguments to get back URLs to the JSON representation. This is the path with .json added on the end. \n These methods each return a datasette.utils.PrefixedUrlString object, which is a subclass of the Python str type. This allows the logic that considers the base_url setting to detect if that prefix has already been applied to the path.", "breadcrumbs": "[\"Internals for plugins\", \"Datasette class\"]", "references": "[]"} {"id": "internals:internals-tracer", "page": "internals", "ref": "internals-tracer", "title": "datasette.tracer", "content": "Running Datasette with --setting trace_debug 1 enables trace debug output, which can then be viewed by adding ?_trace=1 to the query string for any page. \n You can see an example of this at the bottom of latest.datasette.io/fixtures/facetable?_trace=1 . The JSON output shows full details of every SQL query that was executed to generate the page. \n The datasette-pretty-traces plugin can be installed to provide a more readable display of this information. You can see a demo of that here . \n You can add your own custom traces to the JSON output using the trace() context manager. This takes a string that identifies the type of trace being recorded, and records any keyword arguments as additional JSON keys on the resulting trace object. \n The start and end time, duration and a traceback of where the trace was executed will be automatically attached to the JSON object. \n This example uses trace to record the start, end and duration of any HTTP GET requests made using the function: \n from datasette.tracer import trace\nimport httpx\n\n\nasync def fetch_url(url):\n with trace(\"fetch-url\", url=url):\n async with httpx.AsyncClient() as client:\n return await client.get(url)", "breadcrumbs": "[\"Internals for plugins\"]", "references": "[{\"href\": \"https://latest.datasette.io/fixtures/facetable?_trace=1\", \"label\": \"latest.datasette.io/fixtures/facetable?_trace=1\"}, {\"href\": \"https://datasette.io/plugins/datasette-pretty-traces\", \"label\": \"datasette-pretty-traces\"}, {\"href\": \"https://latest-with-plugins.datasette.io/github/commits?_trace=1\", \"label\": \"a demo of that here\"}]"} {"id": "internals:internals-datasette-client", "page": "internals", "ref": "internals-datasette-client", "title": "datasette.client", "content": "Plugins can make internal simulated HTTP requests to the Datasette instance within which they are running. This ensures that all of Datasette's external JSON APIs are also available to plugins, while avoiding the overhead of making an external HTTP call to access those APIs. \n The datasette.client object is a wrapper around the HTTPX Python library , providing an async-friendly API that is similar to the widely used Requests library . \n It offers the following methods: \n \n \n await datasette.client.get(path, **kwargs) - returns HTTPX Response \n \n Execute an internal GET request against that path. \n \n \n \n await datasette.client.post(path, **kwargs) - returns HTTPX Response \n \n Execute an internal POST request. Use data={\"name\": \"value\"} to pass form parameters. \n \n \n \n await datasette.client.options(path, **kwargs) - returns HTTPX Response \n \n Execute an internal OPTIONS request. \n \n \n \n await datasette.client.head(path, **kwargs) - returns HTTPX Response \n \n Execute an internal HEAD request. \n \n \n \n await datasette.client.put(path, **kwargs) - returns HTTPX Response \n \n Execute an internal PUT request. \n \n \n \n await datasette.client.patch(path, **kwargs) - returns HTTPX Response \n \n Execute an internal PATCH request. \n \n \n \n await datasette.client.delete(path, **kwargs) - returns HTTPX Response \n \n Execute an internal DELETE request. \n \n \n \n await datasette.client.request(method, path, **kwargs) - returns HTTPX Response \n \n Execute an internal request with the given HTTP method against that path. \n \n \n \n These methods can be used with datasette.urls - for example: \n table_json = (\n await datasette.client.get(\n datasette.urls.table(\n \"fixtures\", \"facetable\", format=\"json\"\n )\n )\n).json() \n datasette.client methods automatically take the current base_url setting into account, whether or not you use the datasette.urls family of methods to construct the path. \n For documentation on available **kwargs options and the shape of the HTTPX Response object refer to the HTTPX Async documentation .", "breadcrumbs": "[\"Internals for plugins\", \"Datasette class\"]", "references": "[{\"href\": \"https://www.python-httpx.org/\", \"label\": \"HTTPX Python library\"}, {\"href\": \"https://requests.readthedocs.io/\", \"label\": \"Requests library\"}, {\"href\": \"https://www.python-httpx.org/async/\", \"label\": \"HTTPX Async documentation\"}]"} {"id": "performance:performance-hashed-urls", "page": "performance", "ref": "performance-hashed-urls", "title": "datasette-hashed-urls", "content": "If you open a database file in immutable mode using the -i option, you can be assured that the content of that database will not change for the lifetime of the Datasette server. \n The datasette-hashed-urls plugin implements an optimization where your database is served with part of the SHA-256 hash of the database contents baked into the URL. \n A database at /fixtures will instead be served at /fixtures-aa7318b , and a year-long cache expiry header will be returned with those pages. \n This will then be cached by both browsers and caching proxies such as Cloudflare or Fastly, providing a potentially significant performance boost. \n To install the plugin, run the following: \n datasette install datasette-hashed-urls \n \n Prior to Datasette 0.61 hashed URL mode was a core Datasette feature, enabled using the hash_urls setting. This implementation has now been removed in favor of the datasette-hashed-urls plugin. \n Prior to Datasette 0.28 hashed URL mode was the default behaviour for Datasette, since all database files were assumed to be immutable and unchanging. From 0.28 onwards the default has been to treat database files as mutable unless explicitly configured otherwise.", "breadcrumbs": "[\"Performance and caching\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-hashed-urls\", \"label\": \"datasette-hashed-urls plugin\"}]"} {"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: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 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 default_allow_sql Allow anyone to run arbitrary SQL queries\n (default=True)\n allow_download Allow users to download the original SQLite\n database files (default=True)\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-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 --config CONFIG Deprecated: set config option using\n configname:value. Use --setting instead.\n --setting SETTING... Setting, see\n docs.datasette.io/en/stable/settings.html\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 --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 --help Show this message and exit. \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 <TEXT TEXT TEXT>...\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": "changelog:v0-28-publish-cloudrun", "page": "changelog", "ref": "v0-28-publish-cloudrun", "title": "datasette publish cloudrun", "content": "Google Cloud Run is a brand new serverless hosting platform from Google, which allows you to build a Docker container which will run only when HTTP traffic is received and will shut down (and hence cost you nothing) the rest of the time. It's similar to Zeit's Now v1 Docker hosting platform which sadly is no longer accepting signups from new users. \n The new datasette publish cloudrun command was contributed by Romain Primet ( #434 ) and publishes selected databases to a new Datasette instance running on Google Cloud Run. \n See Publishing to Google Cloud Run for full documentation.", "breadcrumbs": "[\"Changelog\", \"0.28 (2019-05-19)\"]", "references": "[{\"href\": \"https://cloud.google.com/run/\", \"label\": \"Google Cloud Run\"}, {\"href\": \"https://hyperion.alpha.spectrum.chat/zeit/now/cannot-create-now-v1-deployments~d206a0d4-5835-4af5-bb5c-a17f0171fb25?m=MTU0Njk2NzgwODM3OA==\", \"label\": \"no longer accepting signups\"}, {\"href\": \"https://github.com/simonw/datasette/pull/434\", \"label\": \"#434\"}]"} {"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 <TEXT TEXT TEXT>...\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": "publish:cli-publish", "page": "publish", "ref": "cli-publish", "title": "datasette publish", "content": "Once you have created a SQLite database (e.g. using csvs-to-sqlite ) you can deploy it to a hosting account using a single command. \n You will need a hosting account with Heroku or Google Cloud . Once you have created your account you will need to install and configure the heroku or gcloud command-line tools.", "breadcrumbs": "[\"Publishing data\"]", "references": "[{\"href\": \"https://github.com/simonw/csvs-to-sqlite/\", \"label\": \"csvs-to-sqlite\"}, {\"href\": \"https://www.heroku.com/\", \"label\": \"Heroku\"}, {\"href\": \"https://cloud.google.com/\", \"label\": \"Google Cloud\"}]"} {"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-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 --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": "publish:cli-package", "page": "publish", "ref": "cli-package", "title": "datasette package", "content": "If you have docker installed (e.g. using Docker for Mac ) you can use the datasette package command to create a new Docker image in your local repository containing the datasette app bundled together with one or more SQLite databases: \n datasette package mydatabase.db \n Here's example output for the package command: \n $ datasette package parlgov.db --extra-options=\"--setting sql_time_limit_ms 2500\"\nSending build context to Docker daemon 4.459MB\nStep 1/7 : FROM python:3.11.0-slim-bullseye\n ---> 79e1dc9af1c1\nStep 2/7 : COPY . /app\n ---> Using cache\n ---> cd4ec67de656\nStep 3/7 : WORKDIR /app\n ---> Using cache\n ---> 139699e91621\nStep 4/7 : RUN pip install datasette\n ---> Using cache\n ---> 340efa82bfd7\nStep 5/7 : RUN datasette inspect parlgov.db --inspect-file inspect-data.json\n ---> Using cache\n ---> 5fddbe990314\nStep 6/7 : EXPOSE 8001\n ---> Using cache\n ---> 8e83844b0fed\nStep 7/7 : CMD datasette serve parlgov.db --port 8001 --inspect-file inspect-data.json --setting sql_time_limit_ms 2500\n ---> Using cache\n ---> 1bd380ea8af3\nSuccessfully built 1bd380ea8af3 \n You can now run the resulting container like so: \n docker run -p 8081:8001 1bd380ea8af3 \n This exposes port 8001 inside the container as port 8081 on your host machine, so you can access the application at http://localhost:8081/ \n You can customize the port that is exposed by the container using the --port option: \n datasette package mydatabase.db --port 8080 \n A full list of options can be seen by running datasette package --help : \n See datasette package for the full list of options for this command.", "breadcrumbs": "[\"Publishing data\"]", "references": "[{\"href\": \"https://www.docker.com/docker-mac\", \"label\": \"Docker for Mac\"}]"} {"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-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 --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-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-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 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 along...\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-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 The exit code 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": "plugin_hooks:plugin-hook-database-actions", "page": "plugin_hooks", "ref": "plugin-hook-database-actions", "title": "database_actions(datasette, actor, database, request)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n actor - dictionary or None \n \n The currently authenticated actor . \n \n \n \n database - string \n \n The name of the database. \n \n \n \n request - Request object \n \n The current HTTP request. \n \n \n \n This hook is similar to table_actions(datasette, actor, database, table, request) but populates an actions menu on the database page. \n Example: datasette-graphql", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-graphql\", \"label\": \"datasette-graphql\"}]"} {"id": "plugin_hooks:plugin-hook-canned-queries", "page": "plugin_hooks", "ref": "plugin-hook-canned-queries", "title": "canned_queries(datasette, database, actor)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n database - string \n \n The name of the database. \n \n \n \n actor - dictionary or None \n \n The currently authenticated actor . \n \n \n \n Use this hook to return a dictionary of additional canned query definitions for the specified database. The return value should be the same shape as the JSON described in the canned query documentation. \n from datasette import hookimpl\n\n\n@hookimpl\ndef canned_queries(datasette, database):\n if database == \"mydb\":\n return {\n \"my_query\": {\n \"sql\": \"select * from my_table where id > :min_id\"\n }\n } \n The hook can alternatively return an awaitable function that returns a list. Here's an example that returns queries that have been stored in the saved_queries database table, if one exists: \n from datasette import hookimpl\n\n\n@hookimpl\ndef canned_queries(datasette, database):\n async def inner():\n db = datasette.get_database(database)\n if await db.table_exists(\"saved_queries\"):\n results = await db.execute(\n \"select name, sql from saved_queries\"\n )\n return {\n result[\"name\"]: {\"sql\": result[\"sql\"]}\n for result in results\n }\n\n return inner \n The actor parameter can be used to include the currently authenticated actor in your decision. Here's an example that returns saved queries that were saved by that actor: \n from datasette import hookimpl\n\n\n@hookimpl\ndef canned_queries(datasette, database, actor):\n async def inner():\n db = datasette.get_database(database)\n if actor is not None and await db.table_exists(\n \"saved_queries\"\n ):\n results = await db.execute(\n \"select name, sql from saved_queries where actor_id = :id\",\n {\"id\": actor[\"id\"]},\n )\n return {\n result[\"name\"]: {\"sql\": result[\"sql\"]}\n for result in results\n }\n\n return inner \n Example: datasette-saved-queries", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-saved-queries\", \"label\": \"datasette-saved-queries\"}]"} {"id": "settings:setting-cache-size-kb", "page": "settings", "ref": "setting-cache-size-kb", "title": "cache_size_kb", "content": "Sets the amount of memory SQLite uses for its per-connection cache , in KB. \n datasette mydatabase.db --setting cache_size_kb 5000", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[{\"href\": \"https://www.sqlite.org/pragma.html#pragma_cache_size\", \"label\": \"per-connection cache\"}]"} {"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": "settings:setting-base-url", "page": "settings", "ref": "setting-base-url", "title": "base_url", "content": "If you are running Datasette behind a proxy, it may be useful to change the root path used for the Datasette instance. \n For example, if you are sending traffic from https://www.example.com/tools/datasette/ through to a proxied Datasette instance you may wish Datasette to use /tools/datasette/ as its root URL. \n You can do that like so: \n datasette mydatabase.db --setting base_url /tools/datasette/", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "internals:internals-utils-await-me-maybe", "page": "internals", "ref": "internals-utils-await-me-maybe", "title": "await_me_maybe(value)", "content": "Utility function for calling await on a return value if it is awaitable, otherwise returning the value. This is used by Datasette to support plugin hooks that can optionally return awaitable functions. Read more about this function in The \u201cawait me maybe\u201d pattern for Python asyncio . \n \n \n async datasette.utils. await_me_maybe value : Any Any \n \n If value is callable, call it. If awaitable, await it. Otherwise return it.", "breadcrumbs": "[\"Internals for plugins\", \"The datasette.utils module\"]", "references": "[{\"href\": \"https://simonwillison.net/2020/Sep/2/await-me-maybe/\", \"label\": \"The \u201cawait me maybe\u201d pattern for Python asyncio\"}]"} {"id": "internals:database-execute-write-script", "page": "internals", "ref": "database-execute-write-script", "title": "await db.execute_write_script(sql, block=True)", "content": "Like execute_write() but can be used to send multiple SQL statements in a single string separated by semicolons, using the sqlite3 conn.executescript() method.", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[{\"href\": \"https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.executescript\", \"label\": \"conn.executescript()\"}]"} {"id": "internals:database-execute-write-many", "page": "internals", "ref": "database-execute-write-many", "title": "await db.execute_write_many(sql, params_seq, block=True)", "content": "Like execute_write() but uses the sqlite3 conn.executemany() method. This will efficiently execute the same SQL statement against each of the parameters in the params_seq iterator, for example: \n await db.execute_write_many(\n \"insert into characters (id, name) values (?, ?)\",\n [(1, \"Melanie\"), (2, \"Selma\"), (2, \"Viktor\")],\n)", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[{\"href\": \"https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.executemany\", \"label\": \"conn.executemany()\"}]"} {"id": "internals:database-execute-write-fn", "page": "internals", "ref": "database-execute-write-fn", "title": "await db.execute_write_fn(fn, block=True)", "content": "This method works like .execute_write() , but instead of a SQL statement you give it a callable Python function. Your function will be queued up and then called when the write connection is available, passing that connection as the argument to the function. \n The function can then perform multiple actions, safe in the knowledge that it has exclusive access to the single writable connection for as long as it is executing. \n \n fn needs to be a regular function, not an async def function. \n \n For example: \n def delete_and_return_count(conn):\n conn.execute(\"delete from some_table where id > 5\")\n return conn.execute(\n \"select count(*) from some_table\"\n ).fetchone()[0]\n\n\ntry:\n num_rows_left = await database.execute_write_fn(\n delete_and_return_count\n )\nexcept Exception as e:\n print(\"An error occurred:\", e) \n The value returned from await database.execute_write_fn(...) will be the return value from your function. \n If your function raises an exception that exception will be propagated up to the await line. \n If you specify block=False the method becomes fire-and-forget, queueing your function to be executed and then allowing your code after the call to .execute_write_fn() to continue running while the underlying thread waits for an opportunity to run your function. A UUID representing the queued task will be returned. Any exceptions in your code will be silently swallowed.", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[]"} {"id": "internals:database-execute-write", "page": "internals", "ref": "database-execute-write", "title": "await db.execute_write(sql, params=None, block=True)", "content": "SQLite only allows one database connection to write at a time. Datasette handles this for you by maintaining a queue of writes to be executed against a given database. Plugins can submit write operations to this queue and they will be executed in the order in which they are received. \n This method can be used to queue up a non-SELECT SQL query to be executed against a single write connection to the database. \n You can pass additional SQL parameters as a tuple or dictionary. \n The method will block until the operation is completed, and the return value will be the return from calling conn.execute(...) using the underlying sqlite3 Python library. \n If you pass block=False this behaviour changes to \"fire and forget\" - queries will be added to the write queue and executed in a separate thread while your code can continue to do other things. The method will return a UUID representing the queued task.", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[]"} {"id": "internals:database-execute-fn", "page": "internals", "ref": "database-execute-fn", "title": "await db.execute_fn(fn)", "content": "Executes a given callback function against a read-only database connection running in a thread. The function will be passed a SQLite connection, and the return value from the function will be returned by the await . \n Example usage: \n def get_version(conn):\n return conn.execute(\n \"select sqlite_version()\"\n ).fetchall()[0][0]\n\n\nversion = await db.execute_fn(get_version)", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[]"} {"id": "internals:database-execute", "page": "internals", "ref": "database-execute", "title": "await db.execute(sql, ...)", "content": "Executes a SQL query against the database and returns the resulting rows (see Results ). \n \n \n sql - string (required) \n \n The SQL query to execute. This can include ? or :named parameters. \n \n \n \n params - list or dict \n \n A list or dictionary of values to use for the parameters. List for ? , dictionary for :named . \n \n \n \n truncate - boolean \n \n Should the rows returned by the query be truncated at the maximum page size? Defaults to True , set this to False to disable truncation. \n \n \n \n custom_time_limit - integer ms \n \n A custom time limit for this query. This can be set to a lower value than the Datasette configured default. If a query takes longer than this it will be terminated early and raise a dataette.database.QueryInterrupted exception. \n \n \n \n page_size - integer \n \n Set a custom page size for truncation, over-riding the configured Datasette default. \n \n \n \n log_sql_errors - boolean \n \n Should any SQL errors be logged to the console in addition to being raised as an error? Defaults to True .", "breadcrumbs": "[\"Internals for plugins\", \"Database class\"]", "references": "[]"} {"id": "internals:datasette-render-template", "page": "internals", "ref": "datasette-render-template", "title": "await .render_template(template, context=None, request=None)", "content": "template - string, list of strings or jinja2.Template \n \n The template file to be rendered, e.g. my_plugin.html . Datasette will search for this file first in the --template-dir= location, if it was specified - then in the plugin's bundled templates and finally in Datasette's set of default templates. \n If this is a list of template file names then the first one that exists will be loaded and rendered. \n If this is a Jinja Template object it will be used directly. \n \n \n \n context - None or a Python dictionary \n \n The context variables to pass to the template. \n \n \n \n request - request object or None \n \n If you pass a Datasette request object here it will be made available to the template. \n \n \n \n Renders a Jinja template using Datasette's preconfigured instance of Jinja and returns the resulting string. The template will have access to Datasette's default template functions and any functions that have been made available by other plugins.", "breadcrumbs": "[\"Internals for plugins\", \"Datasette class\"]", "references": "[{\"href\": \"https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.Template\", \"label\": \"Template object\"}, {\"href\": \"https://jinja.palletsprojects.com/en/2.11.x/\", \"label\": \"Jinja template\"}]"} {"id": "internals:datasette-permission-allowed", "page": "internals", "ref": "datasette-permission-allowed", "title": "await .permission_allowed(actor, action, resource=None, default=False)", "content": "actor - dictionary \n \n The authenticated actor. This is usually request.actor . \n \n \n \n action - string \n \n The name of the action that is being permission checked. \n \n \n \n resource - string or tuple, optional \n \n The resource, e.g. the name of the database, or a tuple of two strings containing the name of the database and the name of the table. Only some permissions apply to a resource. \n \n \n \n default - optional, True or False \n \n Should this permission check be default allow or default deny. \n \n \n \n Check if the given actor has permission to perform the given action on the given resource. \n Some permission checks are carried out against rules defined in metadata.json , while other custom permissions may be decided by plugins that implement the permission_allowed(datasette, actor, action, resource) plugin hook. \n If neither metadata.json nor any of the plugins provide an answer to the permission query the default argument will be returned. \n See Built-in permissions for a full list of permission actions included in Datasette core.", "breadcrumbs": "[\"Internals for plugins\", \"Datasette class\"]", "references": "[]"} {"id": "internals:datasette-ensure-permissions", "page": "internals", "ref": "datasette-ensure-permissions", "title": "await .ensure_permissions(actor, permissions)", "content": "actor - dictionary \n \n The authenticated actor. This is usually request.actor . \n \n \n \n permissions - list \n \n A list of permissions to check. Each permission in that list can be a string action name or a 2-tuple of (action, resource) . \n \n \n \n This method allows multiple permissions to be checked at once. It raises a datasette.Forbidden exception if any of the checks are denied before one of them is explicitly granted. \n This is useful when you need to check multiple permissions at once. For example, an actor should be able to view a table if either one of the following checks returns True or not a single one of them returns False : \n await self.ds.ensure_permissions(\n request.actor,\n [\n (\"view-table\", (database, table)),\n (\"view-database\", database),\n \"view-instance\",\n ],\n)", "breadcrumbs": "[\"Internals for plugins\", \"Datasette class\"]", "references": "[]"} {"id": "internals:datasette-check-visibility", "page": "internals", "ref": "datasette-check-visibility", "title": "await .check_visibility(actor, action=None, resource=None, permissions=None)", "content": "actor - dictionary \n \n The authenticated actor. This is usually request.actor . \n \n \n \n action - string, optional \n \n The name of the action that is being permission checked. \n \n \n \n resource - string or tuple, optional \n \n The resource, e.g. the name of the database, or a tuple of two strings containing the name of the database and the name of the table. Only some permissions apply to a resource. \n \n \n \n permissions - list of action strings or (action, resource) tuples, optional \n \n Provide this instead of action and resource to check multiple permissions at once. \n \n \n \n This convenience method can be used to answer the question \"should this item be considered private, in that it is visible to me but it is not visible to anonymous users?\" \n It returns a tuple of two booleans, (visible, private) . visible indicates if the actor can see this resource. private will be True if an anonymous user would not be able to view the resource. \n This example checks if the user can access a specific table, and sets private so that a padlock icon can later be displayed: \n visible, private = await self.ds.check_visibility(\n request.actor,\n action=\"view-table\",\n resource=(database, table),\n) \n The following example runs three checks in a row, similar to await .ensure_permissions(actor, permissions) . If any of the checks are denied before one of them is explicitly granted then visible will be False . private will be True if an anonymous user would not be able to view the resource. \n visible, private = await self.ds.check_visibility(\n request.actor,\n permissions=[\n (\"view-table\", (database, table)),\n (\"view-database\", database),\n \"view-instance\",\n ],\n)", "breadcrumbs": "[\"Internals for plugins\", \"Datasette class\"]", "references": "[]"} {"id": "plugin_hooks:plugin-asgi-wrapper", "page": "plugin_hooks", "ref": "plugin-asgi-wrapper", "title": "asgi_wrapper(datasette)", "content": "Return an ASGI middleware wrapper function that will be applied to the Datasette ASGI application. \n This is a very powerful hook. You can use it to manipulate the entire Datasette response, or even to configure new URL routes that will be handled by your own custom code. \n You can write your ASGI code directly against the low-level specification, or you can use the middleware utilities provided by an ASGI framework such as Starlette . \n This example plugin adds a x-databases HTTP header listing the currently attached databases: \n from datasette import hookimpl\nfrom functools import wraps\n\n\n@hookimpl\ndef asgi_wrapper(datasette):\n def wrap_with_databases_header(app):\n @wraps(app)\n async def add_x_databases_header(\n scope, receive, send\n ):\n async def wrapped_send(event):\n if event[\"type\"] == \"http.response.start\":\n original_headers = (\n event.get(\"headers\") or []\n )\n event = {\n \"type\": event[\"type\"],\n \"status\": event[\"status\"],\n \"headers\": original_headers\n + [\n [\n b\"x-databases\",\n \", \".join(\n datasette.databases.keys()\n ).encode(\"utf-8\"),\n ]\n ],\n }\n await send(event)\n\n await app(scope, receive, wrapped_send)\n\n return add_x_databases_header\n\n return wrap_with_databases_header \n Examples: datasette-cors , datasette-pyinstrument , datasette-total-page-time", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://asgi.readthedocs.io/\", \"label\": \"ASGI\"}, {\"href\": \"https://www.starlette.io/middleware/\", \"label\": \"Starlette\"}, {\"href\": \"https://datasette.io/plugins/datasette-cors\", \"label\": \"datasette-cors\"}, {\"href\": \"https://datasette.io/plugins/datasette-pyinstrument\", \"label\": \"datasette-pyinstrument\"}, {\"href\": \"https://datasette.io/plugins/datasette-total-page-time\", \"label\": \"datasette-total-page-time\"}]"} {"id": "settings:setting-allow-facet", "page": "settings", "ref": "setting-allow-facet", "title": "allow_facet", "content": "Allow users to specify columns they would like to facet on using the ?_facet=COLNAME URL parameter to the table view. \n This is enabled by default. If disabled, facets will still be displayed if they have been specifically enabled in metadata.json configuration for the table. \n Here's how to disable this feature: \n datasette mydatabase.db --setting allow_facet off", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-allow-download", "page": "settings", "ref": "setting-allow-download", "title": "allow_download", "content": "Should users be able to download the original SQLite database using a link on the database index page? This is turned on by default. However, databases can only be downloaded if they are served in immutable mode and not in-memory. If downloading is unavailable for either of these reasons, the download link is hidden even if allow_download is on. To disable database downloads, use the following: \n datasette mydatabase.db --setting allow_download off", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "settings:setting-allow-csv-stream", "page": "settings", "ref": "setting-allow-csv-stream", "title": "allow_csv_stream", "content": "Enables the CSV export feature where an entire table\n (potentially hundreds of thousands of rows) can be exported as a single CSV\n file. This is turned on by default - you can turn it off like this: \n datasette mydatabase.db --setting allow_csv_stream off", "breadcrumbs": "[\"Settings\", \"Settings\"]", "references": "[]"} {"id": "authentication:authentication-actor-matches-allow", "page": "authentication", "ref": "authentication-actor-matches-allow", "title": "actor_matches_allow()", "content": "Plugins that wish to implement this same \"allow\" block permissions scheme can take advantage of the datasette.utils.actor_matches_allow(actor, allow) function: \n from datasette.utils import actor_matches_allow\n\nactor_matches_allow({\"id\": \"root\"}, {\"id\": \"*\"})\n# returns True \n The currently authenticated actor is made available to plugins as request.actor .", "breadcrumbs": "[\"Authentication and permissions\"]", "references": "[]"} {"id": "plugin_hooks:plugin-hook-actor-from-request", "page": "plugin_hooks", "ref": "plugin-hook-actor-from-request", "title": "actor_from_request(datasette, request)", "content": "datasette - Datasette class \n \n You can use this to access plugin configuration options via datasette.plugin_config(your_plugin_name) , or to execute SQL queries. \n \n \n \n request - Request object \n \n The current HTTP request. \n \n \n \n This is part of Datasette's authentication and permissions system . The function should attempt to authenticate an actor (either a user or an API actor of some sort) based on information in the request. \n If it cannot authenticate an actor, it should return None . Otherwise it should return a dictionary representing that actor. \n Here's an example that authenticates the actor based on an incoming API key: \n from datasette import hookimpl\nimport secrets\n\nSECRET_KEY = \"this-is-a-secret\"\n\n\n@hookimpl\ndef actor_from_request(datasette, request):\n authorization = (\n request.headers.get(\"authorization\") or \"\"\n )\n expected = \"Bearer {}\".format(SECRET_KEY)\n\n if secrets.compare_digest(authorization, expected):\n return {\"id\": \"bot\"} \n If you install this in your plugins directory you can test it like this: \n $ curl -H 'Authorization: Bearer this-is-a-secret' http://localhost:8003/-/actor.json \n Instead of returning a dictionary, this function can return an awaitable function which itself returns either None or a dictionary. This is useful for authentication functions that need to make a database query - for example: \n from datasette import hookimpl\n\n\n@hookimpl\ndef actor_from_request(datasette, request):\n async def inner():\n token = request.args.get(\"_token\")\n if not token:\n return None\n # Look up ?_token=xxx in sessions table\n result = await datasette.get_database().execute(\n \"select count(*) from sessions where token = ?\",\n [token],\n )\n if result.first()[0]:\n return {\"token\": token}\n else:\n return None\n\n return inner \n Example: datasette-auth-tokens", "breadcrumbs": "[\"Plugin hooks\"]", "references": "[{\"href\": \"https://datasette.io/plugins/datasette-auth-tokens\", \"label\": \"datasette-auth-tokens\"}]"} {"id": "writing_plugins:writing-plugins-configuration", "page": "writing_plugins", "ref": "writing-plugins-configuration", "title": "Writing plugins that accept configuration", "content": "When you are writing plugins, you can access plugin configuration like this using the datasette plugin_config() method. If you know you need plugin configuration for a specific table, you can access it like this: \n plugin_config = datasette.plugin_config(\n \"datasette-cluster-map\", database=\"sf-trees\", table=\"Street_Tree_List\"\n) \n This will return the {\"latitude_column\": \"lat\", \"longitude_column\": \"lng\"} in the above example. \n If there is no configuration for that plugin, the method will return None . \n If it cannot find the requested configuration at the table layer, it will fall back to the database layer and then the root layer. For example, a user may have set the plugin configuration option like so: \n {\n \"databases: {\n \"sf-trees\": {\n \"plugins\": {\n \"datasette-cluster-map\": {\n \"latitude_column\": \"xlat\",\n \"longitude_column\": \"xlng\"\n }\n }\n }\n }\n} \n In this case, the above code would return that configuration for ANY table within the sf-trees database. \n The plugin configuration could also be set at the top level of metadata.json : \n {\n \"title\": \"This is the top-level title in metadata.json\",\n \"plugins\": {\n \"datasette-cluster-map\": {\n \"latitude_column\": \"xlat\",\n \"longitude_column\": \"xlng\"\n }\n }\n} \n Now that datasette-cluster-map plugin configuration will apply to every table in every database.", "breadcrumbs": "[\"Writing plugins\"]", "references": "[]"} {"id": "writing_plugins:id1", "page": "writing_plugins", "ref": "id1", "title": "Writing plugins", "content": "You can write one-off plugins that apply to just one Datasette instance, or you can write plugins which can be installed using pip and can be shipped to the Python Package Index ( PyPI ) for other people to install. \n Want to start by looking at an example? The Datasette plugins directory lists more than 90 open source plugins with code you can explore. The plugin hooks page includes links to example plugins for each of the documented hooks.", "breadcrumbs": "[]", "references": "[{\"href\": \"https://pypi.org/\", \"label\": \"PyPI\"}, {\"href\": \"https://datasette.io/plugins\", \"label\": \"Datasette plugins directory\"}]"} {"id": "writing_plugins:writing-plugins-one-off", "page": "writing_plugins", "ref": "writing-plugins-one-off", "title": "Writing one-off plugins", "content": "The quickest way to start writing a plugin is to create a my_plugin.py file and drop it into your plugins/ directory. Here is an example plugin, which adds a new custom SQL function called hello_world() which takes no arguments and returns the string Hello world! . \n from datasette import hookimpl\n\n\n@hookimpl\ndef prepare_connection(conn):\n conn.create_function(\n \"hello_world\", 0, lambda: \"Hello world!\"\n ) \n If you save this in plugins/my_plugin.py you can then start Datasette like this: \n datasette serve mydb.db --plugins-dir=plugins/ \n Now you can navigate to http://localhost:8001/mydb and run this SQL: \n select hello_world(); \n To see the output of your plugin.", "breadcrumbs": "[\"Writing plugins\"]", "references": "[{\"href\": \"http://localhost:8001/mydb\", \"label\": \"http://localhost:8001/mydb\"}]"} {"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": "sql_queries:canned-queries-writable", "page": "sql_queries", "ref": "canned-queries-writable", "title": "Writable canned queries", "content": "Canned queries by default are read-only. You can use the \"write\": true key to indicate that a canned query can write to the database. \n See Controlling access to specific canned queries for details on how to add permission checks to canned queries, using the \"allow\" key. \n {\n \"databases\": {\n \"mydatabase\": {\n \"queries\": {\n \"add_name\": {\n \"sql\": \"INSERT INTO names (name) VALUES (:name)\",\n \"write\": true\n }\n }\n }\n }\n} \n This configuration will create a page at /mydatabase/add_name displaying a form with a name field. Submitting that form will execute the configured INSERT query. \n You can customize how Datasette represents success and errors using the following optional properties: \n \n \n on_success_message - the message shown when a query is successful \n \n \n on_success_redirect - the path or URL the user is redirected to on success \n \n \n on_error_message - the message shown when a query throws an error \n \n \n on_error_redirect - the path or URL the user is redirected to on error \n \n \n For example: \n {\n \"databases\": {\n \"mydatabase\": {\n \"queries\": {\n \"add_name\": {\n \"sql\": \"INSERT INTO names (name) VALUES (:name)\",\n \"write\": true,\n \"on_success_message\": \"Name inserted\",\n \"on_success_redirect\": \"/mydatabase/names\",\n \"on_error_message\": \"Name insert failed\",\n \"on_error_redirect\": \"/mydatabase\"\n }\n }\n }\n }\n} \n You can use \"params\" to explicitly list the named parameters that should be displayed as form fields - otherwise they will be automatically detected. \n You can pre-populate form fields when the page first loads using a query string, e.g. /mydatabase/add_name?name=Prepopulated . The user will have to submit the form to execute the query.", "breadcrumbs": "[\"Running SQL queries\", \"Canned queries\"]", "references": "[]"}