{"id": "internals:internals-tracer-trace-child-tasks", "page": "internals", "ref": "internals-tracer-trace-child-tasks", "title": "Tracing child tasks", "content": "If your code uses a mechanism such as asyncio.gather() to execute code in additional tasks you may find that some of the traces are missing from the display. \n You can use the trace_child_tasks() context manager to ensure these child tasks are correctly handled. \n from datasette import tracer\n\nwith tracer.trace_child_tasks():\n results = await asyncio.gather(\n # ... async tasks here\n ) \n This example uses the register_routes() plugin hook to add a page at /parallel-queries which executes two SQL queries in parallel using asyncio.gather() and returns their results. \n from datasette import hookimpl\nfrom datasette import tracer\n\n\n@hookimpl\ndef register_routes():\n async def parallel_queries(datasette):\n db = datasette.get_database()\n with tracer.trace_child_tasks():\n one, two = await asyncio.gather(\n db.execute(\"select 1\"),\n db.execute(\"select 2\"),\n )\n return Response.json(\n {\n \"one\": one.single_value(),\n \"two\": two.single_value(),\n }\n )\n\n return [\n (r\"/parallel-queries$\", parallel_queries),\n ] \n Adding ?_trace=1 will show that the trace covers both of those child tasks.", "breadcrumbs": "[\"Internals for plugins\", \"datasette.tracer\"]", "references": "[]"} {"id": "full_text_search:configuring-fts-using-csvs-to-sqlite", "page": "full_text_search", "ref": "configuring-fts-using-csvs-to-sqlite", "title": "Configuring FTS using csvs-to-sqlite", "content": "If your data starts out in CSV files, you can use Datasette's companion tool csvs-to-sqlite to convert that file into a SQLite database and enable full-text search on specific columns. For a file called items.csv where you want full-text search to operate against the name and description columns you would run the following: \n $ csvs-to-sqlite items.csv items.db -f name -f description", "breadcrumbs": "[\"Full-text search\", \"Enabling full-text search for a SQLite table\"]", "references": "[{\"href\": \"https://github.com/simonw/csvs-to-sqlite\", \"label\": \"csvs-to-sqlite\"}]"} {"id": "performance:http-caching", "page": "performance", "ref": "http-caching", "title": "HTTP caching", "content": "If your database is immutable and guaranteed not to change, you can gain major performance improvements from Datasette by enabling HTTP caching. \n This can work at two different levels. First, it can tell browsers to cache the results of queries and serve future requests from the browser cache. \n More significantly, it allows you to run Datasette behind a caching proxy such as Varnish or use a cache provided by a hosted service such as Fastly or Cloudflare . This can provide incredible speed-ups since a query only needs to be executed by Datasette the first time it is accessed - all subsequent hits can then be served by the cache. \n Using a caching proxy in this way could enable a Datasette-backed visualization to serve thousands of hits a second while running Datasette itself on extremely inexpensive hosting. \n Datasette's integration with HTTP caches can be enabled using a combination of configuration options and query string arguments. \n The default_cache_ttl setting sets the default HTTP cache TTL for all Datasette pages. This is 5 seconds unless you change it - you can set it to 0 if you wish to disable HTTP caching entirely. \n You can also change the cache timeout on a per-request basis using the ?_ttl=10 query string parameter. This can be useful when you are working with the Datasette JSON API - you may decide that a specific query can be cached for a longer time, or maybe you need to set ?_ttl=0 for some requests for example if you are running a SQL order by random() query.", "breadcrumbs": "[\"Performance and caching\"]", "references": "[{\"href\": \"https://varnish-cache.org/\", \"label\": \"Varnish\"}, {\"href\": \"https://www.fastly.com/\", \"label\": \"Fastly\"}, {\"href\": \"https://www.cloudflare.com/\", \"label\": \"Cloudflare\"}]"} {"id": "writing_plugins:writing-plugins-static-assets", "page": "writing_plugins", "ref": "writing-plugins-static-assets", "title": "Static assets", "content": "If your plugin has a static/ directory, Datasette will automatically configure itself to serve those static assets from the following path: \n /-/static-plugins/NAME_OF_PLUGIN_PACKAGE/yourfile.js \n Use the datasette.urls.static_plugins(plugin_name, path) method to generate URLs to that asset that take the base_url setting into account, see datasette.urls . \n To bundle the static assets for a plugin in the package that you publish to PyPI, add the following to the plugin's setup.py : \n package_data = (\n {\n \"datasette_plugin_name\": [\n \"static/plugin.js\",\n ],\n },\n) \n Where datasette_plugin_name is the name of the plugin package (note that it uses underscores, not hyphens) and static/plugin.js is the path within that package to the static file. \n datasette-cluster-map is a useful example of a plugin that includes packaged static assets in this way.", "breadcrumbs": "[\"Writing plugins\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette-cluster-map\", \"label\": \"datasette-cluster-map\"}]"} {"id": "writing_plugins:writing-plugins-custom-templates", "page": "writing_plugins", "ref": "writing-plugins-custom-templates", "title": "Custom templates", "content": "If your plugin has a templates/ directory, Datasette will attempt to load templates from that directory before it uses its own default templates. \n The priority order for template loading is: \n \n \n templates from the --template-dir argument, if specified \n \n \n templates from the templates/ directory in any installed plugins \n \n \n default templates that ship with Datasette \n \n \n See Custom pages and templates for more details on how to write custom templates, including which filenames to use to customize which parts of the Datasette UI. \n Templates should be bundled for distribution using the same package_data mechanism in setup.py described for static assets above, for example: \n package_data = (\n {\n \"datasette_plugin_name\": [\n \"templates/my_template.html\",\n ],\n },\n) \n You can also use wildcards here such as templates/*.html . See datasette-edit-schema for an example of this pattern.", "breadcrumbs": "[\"Writing plugins\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette-edit-schema\", \"label\": \"datasette-edit-schema\"}]"} {"id": "testing_plugins:testing-plugins-pytest-httpx", "page": "testing_plugins", "ref": "testing-plugins-pytest-httpx", "title": "Testing outbound HTTP calls with pytest-httpx", "content": "If your plugin makes outbound HTTP calls - for example datasette-auth-github or datasette-import-table - you may need to mock those HTTP requests in your tests. \n The pytest-httpx package is a useful library for mocking calls. It can be tricky to use with Datasette though since it mocks all HTTPX requests, and Datasette's own testing mechanism uses HTTPX internally. \n To avoid breaking your tests, you can return [\"localhost\"] from the non_mocked_hosts() fixture. \n As an example, here's a very simple plugin which executes an HTTP response and returns the resulting content: \n from datasette import hookimpl\nfrom datasette.utils.asgi import Response\nimport httpx\n\n\n@hookimpl\ndef register_routes():\n return [\n (r\"^/-/fetch-url$\", fetch_url),\n ]\n\n\nasync def fetch_url(datasette, request):\n if request.method == \"GET\":\n return Response.html(\n \"\"\"\n
\n \n \n
\"\"\".format(\n request.scope[\"csrftoken\"]()\n )\n )\n vars = await request.post_vars()\n url = vars[\"url\"]\n return Response.text(httpx.get(url).text) \n Here's a test for that plugin that mocks the HTTPX outbound request: \n from datasette.app import Datasette\nimport pytest\n\n\n@pytest.fixture\ndef non_mocked_hosts():\n # This ensures httpx-mock will not affect Datasette's own\n # httpx calls made in the tests by datasette.client:\n return [\"localhost\"]\n\n\nasync def test_outbound_http_call(httpx_mock):\n httpx_mock.add_response(\n url=\"https://www.example.com/\",\n text=\"Hello world\",\n )\n datasette = Datasette([], memory=True)\n response = await datasette.client.post(\n \"/-/fetch-url\",\n data={\"url\": \"https://www.example.com/\"},\n )\n assert response.text == \"Hello world\"\n\n outbound_request = httpx_mock.get_request()\n assert (\n outbound_request.url == \"https://www.example.com/\"\n )", "breadcrumbs": "[\"Testing plugins\"]", "references": "[{\"href\": \"https://pypi.org/project/pytest-httpx/\", \"label\": \"pytest-httpx\"}]"} {"id": "changelog:id42", "page": "changelog", "ref": "id42", "title": "0.51.1 (2020-10-31)", "content": "Improvements to the new Binary data documentation page.", "breadcrumbs": "[\"Changelog\"]", "references": "[]"} {"id": "internals:internals-response-asgi-send", "page": "internals", "ref": "internals-response-asgi-send", "title": "Returning a response with .asgi_send(send)", "content": "In most cases you will return Response objects from your own view functions. You can also use a Response instance to respond at a lower level via ASGI, for example if you are writing code that uses the asgi_wrapper(datasette) hook. \n Create a Response object and then use await response.asgi_send(send) , passing the ASGI send function. For example: \n async def require_authorization(scope, receive, send):\n response = Response.text(\n \"401 Authorization Required\",\n headers={\n \"www-authenticate\": 'Basic realm=\"Datasette\", charset=\"UTF-8\"'\n },\n status=401,\n )\n await response.asgi_send(send)", "breadcrumbs": "[\"Internals for plugins\", \"Response class\"]", "references": "[]"} {"id": "changelog:id17", "page": "changelog", "ref": "id17", "title": "0.61 (2022-03-23)", "content": "In preparation for Datasette 1.0, this release includes two potentially backwards-incompatible changes. Hashed URL mode has been moved to a separate plugin, and the way Datasette generates URLs to databases and tables with special characters in their name such as / and . has changed. \n Datasette also now requires Python 3.7 or higher. \n \n \n URLs within Datasette now use a different encoding scheme for tables or databases that include \"special\" characters outside of the range of a-zA-Z0-9_- . This scheme is explained here: Tilde encoding . ( #1657 ) \n \n \n Removed hashed URL mode from Datasette. The new datasette-hashed-urls plugin can be used to achieve the same result, see datasette-hashed-urls for details. ( #1661 ) \n \n \n Databases can now have a custom path within the Datasette instance that is independent of the database name, using the db.route property. ( #1668 ) \n \n \n Datasette is now covered by a Code of Conduct . ( #1654 ) \n \n \n Python 3.6 is no longer supported. ( #1577 ) \n \n \n Tests now run against Python 3.11-dev. ( #1621 ) \n \n \n New datasette.ensure_permissions(actor, permissions) internal method for checking multiple permissions at once. ( #1675 ) \n \n \n New datasette.check_visibility(actor, action, resource=None) internal method for checking if a user can see a resource that would otherwise be invisible to unauthenticated users. ( #1678 ) \n \n \n Table and row HTML pages now include a element and return a Link: URL; rel=\"alternate\"; type=\"application/json+datasette\" HTTP header pointing to the JSON version of those pages. ( #1533 ) \n \n \n Access-Control-Expose-Headers: Link is now added to the CORS headers, allowing remote JavaScript to access that header. \n \n \n Canned queries are now shown at the top of the database page, directly below the SQL editor. Previously they were shown at the bottom, below the list of tables. ( #1612 ) \n \n \n Datasette now has a default favicon. ( #1603 ) \n \n \n sqlite_stat tables are now hidden by default. ( #1587 ) \n \n \n SpatiaLite tables data_licenses , KNN and KNN2 are now hidden by default. ( #1601 ) \n \n \n SQL query tracing mechanism now works for queries executed in asyncio sub-tasks, such as those created by asyncio.gather() . ( #1576 ) \n \n \n datasette.tracer mechanism is now documented. \n \n \n Common Datasette symbols can now be imported directly from the top-level datasette package, see Import shortcuts . Those symbols are Response , Forbidden , NotFound , hookimpl , actor_matches_allow . ( #957 ) \n \n \n /-/versions page now returns additional details for libraries used by SpatiaLite. ( #1607 ) \n \n \n Documentation now links to the Datasette Tutorials . \n \n \n Datasette will now also look for SpatiaLite in /opt/homebrew - thanks, Dan Peterson. ( #1649 ) \n \n \n Fixed bug where custom pages did not work on Windows. Thanks, Robert Christie. ( #1545 ) \n \n \n Fixed error caused when a table had a column named n . ( #1228 )", "breadcrumbs": "[\"Changelog\"]", "references": "[{\"href\": \"https://github.com/simonw/datasette/issues/1657\", \"label\": \"#1657\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1661\", \"label\": \"#1661\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1668\", \"label\": \"#1668\"}, {\"href\": \"https://github.com/simonw/datasette/blob/main/CODE_OF_CONDUCT.md\", \"label\": \"Code of Conduct\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1654\", \"label\": \"#1654\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1577\", \"label\": \"#1577\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1621\", \"label\": \"#1621\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1675\", \"label\": \"#1675\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1678\", \"label\": \"#1678\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1533\", \"label\": \"#1533\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1612\", \"label\": \"#1612\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1603\", \"label\": \"#1603\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1587\", \"label\": \"#1587\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1601\", \"label\": \"#1601\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1576\", \"label\": \"#1576\"}, {\"href\": \"https://github.com/simonw/datasette/issues/957\", \"label\": \"#957\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1607\", \"label\": \"#1607\"}, {\"href\": \"https://datasette.io/tutorials\", \"label\": \"Datasette Tutorials\"}, {\"href\": \"https://github.com/simonw/datasette/pull/1649\", \"label\": \"#1649\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1545\", \"label\": \"#1545\"}, {\"href\": \"https://github.com/simonw/datasette/issues/1228\", \"label\": \"#1228\"}]"} {"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": "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": "internals:internals-database", "page": "internals", "ref": "internals-database", "title": "Database class", "content": "Instances of the Database class can be used to execute queries against attached SQLite databases, and to run introspection against their schemas.", "breadcrumbs": "[\"Internals for plugins\"]", "references": "[]"} {"id": "changelog:javascript-modules", "page": "changelog", "ref": "javascript-modules", "title": "JavaScript modules", "content": "JavaScript modules were introduced in ECMAScript 2015 and provide native browser support for the import and export keywords. \n To use modules, JavaScript needs to be included in