sections
23 rows where breadcrumbs contains "Datasette 1.0a20 plugin upgrade guide" and references = "[]"
This data as json, CSV (advanced)
Suggested facets: page, ref, title, content, breadcrumbs, breadcrumbs (array)
| id ▼ | page | ref | title | content | breadcrumbs | references |
|---|---|---|---|---|---|---|
| upgrade-1.0a20:fixing-async-with-httpx-asyncclient-app-app | upgrade-1.0a20 | fixing-async-with-httpx-asyncclient-app-app | Fixing async with httpx.AsyncClient(app=app) | Some older plugins may use the following pattern in their tests, which is no longer supported: app = Datasette([], memory=True).app() async with httpx.AsyncClient(app=app) as client: response = await client.get("http://localhost/path") The new pattern is to use ds.client like this: ds = Datasette([], memory=True) response = await ds.client.get("/path") | ["Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade-1.0a20:migrating-from-metadata-to-config | upgrade-1.0a20 | migrating-from-metadata-to-config | Migrating from metadata= to config= | Datasette 1.0 separates metadata (titles, descriptions, licenses) from configuration (settings, plugins, queries, permissions). Plugin tests and code need to be updated accordingly. | ["Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade-1.0a20:permissions-are-now-actions | upgrade-1.0a20 | permissions-are-now-actions | Permissions are now actions | The register_permissions() hook shoud be replaced with register_actions() . Old code: @hookimpl def register_permissions(datasette): return [ Permission( name="explain-sql", abbr=None, description="Can explain SQL queries", takes_database=True, takes_resource=False, default=False, ), Permission( name="annotate-rows", abbr=None, description="Can annotate rows", takes_database=True, takes_resource=True, default=False, ), Permission( name="view-debug-info", abbr=None, description="Can view debug information", takes_database=False, takes_resource=False, default=False, ), ] The new Action does not have a default= parameter. Here's the equivalent new code: from datasette import hookimpl from datasette.permissions import Action from datasette.resources import DatabaseResource, TableResource @hookimpl def register_actions(datasette): return [ Action( name="explain-sql", description="Explain SQL queries", resource_class=DatabaseResource, ), Action( name="annotate-rows", description="Annotate rows", resource_class=TableResource, ), Action( name="view-debug-info", description="View debug information", ), ] The abbr= is now optional and defaults to None . For actions that apply to specific resources (like databases or tables), specify the resource_class instead of takes_parent and takes_child . Note that view-debug-info does not specify a resource_class because it applies globally. | ["Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade-1.0a20:root-enabled-instances-during-testing | upgrade-1.0a20 | root-enabled-instances-during-testing | Root-enabled instances during testing | When writing tests that exercise root-only functionality, make sure to set datasette.root_enabled = True on the Datasette instance. Root permissions are only granted automatically when Datasette is started with datasette --root or when the flag is enabled directly in tests. | ["Datasette 1.0a20 plugin upgrade guide", "Root user checks are no longer necessary"] | [] |
| upgrade-1.0a20:root-user-checks-are-no-longer-necessary | upgrade-1.0a20 | root-user-checks-are-no-longer-necessary | Root user checks are no longer necessary | Some plugins would introduce their own custom permission and then ensure the "root" actor had access to it using a pattern like this: @hookimpl def register_permissions(datasette): return [ Permission( name="upload-dbs", abbr=None, description="Upload SQLite database files", takes_database=False, takes_resource=False, default=False, ) ] @hookimpl def permission_allowed(actor, action): if action == "upload-dbs" and actor and actor.get("id") == "root": return True This is no longer necessary in Datasette 1.0a20 - the "root" actor automatically has all permissions when Datasette is started with the datasette --root option. The permission_allowed() hook in this example can be entirely removed. | ["Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade-1.0a20:target-the-new-apis-exclusively | upgrade-1.0a20 | target-the-new-apis-exclusively | Target the new APIs exclusively | Datasette 1.0a20’s permission system is substantially different from previous releases. Attempting to keep plugin code compatible with both the old permission_allowed() and the new allowed() interfaces leads to brittle workarounds. Prefer to adopt the 1.0a20 APIs ( register_actions , permission_resources_sql() , and datasette.allowed() ) outright and drop legacy fallbacks. | ["Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade-1.0a20:update-datasette-metadata-calls | upgrade-1.0a20 | update-datasette-metadata-calls | Update datasette.metadata() calls | The datasette.metadata() method has been removed. Use these methods instead: Old code: try: title = datasette.metadata(database=database)["queries"][query_name]["title"] except (KeyError, TypeError): pass New code: try: query_info = await datasette.get_canned_query(database, query_name, request.actor) if query_info and "title" in query_info: title = query_info["title"] except (KeyError, TypeError): pass | ["Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade-1.0a20:update-query-urls-in-tests | upgrade-1.0a20 | update-query-urls-in-tests | Update query URLs in tests | Datasette now redirects ?sql= parameters from database pages to the query view: Old code: response = await ds.client.get("/_memory.atom?sql=select+1") New code: response = await ds.client.get("/_memory/-/query.atom?sql=select+1") | ["Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade-1.0a20:update-render-functions-to-async | upgrade-1.0a20 | update-render-functions-to-async | Update render functions to async | If your plugin's render function needs to call datasette.get_canned_query() or other async Datasette methods, it must be declared as async: Old code: def render_atom(datasette, request, sql, columns, rows, database, table, query_name, view_name, data): # ... if query_name: title = datasette.metadata(database=database)["queries"][query_name]["title"] New code: async def render_atom(datasette, request, sql, columns, rows, database, table, query_name, view_name, data): # ... if query_name: query_info = await datasette.get_canned_query(database, query_name, request.actor) if query_info and "title" in query_info: title = query_info["title"] | ["Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade-1.0a20:update-test-constructors | upgrade-1.0a20 | update-test-constructors | Update test constructors | Old code: ds = Datasette( memory=True, metadata={ "databases": { "_memory": {"queries": {"my_query": {"sql": "select 1", "title": "My Query"}}} }, "plugins": { "my-plugin": {"setting": "value"} } } ) New code: ds = Datasette( memory=True, config={ "databases": { "_memory": {"queries": {"my_query": {"sql": "select 1", "title": "My Query"}}} }, "plugins": { "my-plugin": {"setting": "value"} } } ) | ["Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade-1.0a20:using-datasette-allowed-to-check-permissions-instead-of-datasette-permission-allowed | upgrade-1.0a20 | using-datasette-allowed-to-check-permissions-instead-of-datasette-permission-allowed | Using datasette.allowed() to check permissions instead of datasette.permission_allowed() | The internal method datasette.permission_allowed() has been replaced by datasette.allowed() . The old method looked like this: can_debug = await datasette.permission_allowed( request.actor, "view-debug-info", ) can_explain_sql = await datasette.permission_allowed( request.actor, "explain-sql", resource="database_name", ) can_annotate_rows = await datasette.permission_allowed( request.actor, "annotate-rows", resource=(database_name, table_name), ) Note the confusing design here where resource could be either a string or a tuple depending on the permission being checked. The new keyword-only design makes this a lot more clear: from datasette.resources import DatabaseResource, TableResource can_debug = await datasette.allowed( actor=request.actor, action="view-debug-info", ) can_explain_sql = await datasette.allowed( actor=request.actor, action="explain-sql", resource=DatabaseResource(database_name), ) can_annotate_rows = await datasette.allowed( actor=request.actor, action="annotate-rows", resource=TableResource(database_name, table_name), ) | ["Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade_guide:fixing-async-with-httpx-asyncclient-app-app | upgrade_guide | fixing-async-with-httpx-asyncclient-app-app | Fixing async with httpx.AsyncClient(app=app) | Some older plugins may use the following pattern in their tests, which is no longer supported: app = Datasette([], memory=True).app() async with httpx.AsyncClient(app=app) as client: response = await client.get("http://localhost/path") The new pattern is to use ds.client like this: ds = Datasette([], memory=True) response = await ds.client.get("/path") | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade_guide:migrating-from-metadata-to-config | upgrade_guide | migrating-from-metadata-to-config | Migrating from metadata= to config= | Datasette 1.0 separates metadata (titles, descriptions, licenses) from configuration (settings, plugins, queries, permissions). Plugin tests and code need to be updated accordingly. | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade_guide:permissions-are-now-actions | upgrade_guide | permissions-are-now-actions | Permissions are now actions | The register_permissions() hook shoud be replaced with register_actions() . Old code: @hookimpl def register_permissions(datasette): return [ Permission( name="explain-sql", abbr=None, description="Can explain SQL queries", takes_database=True, takes_resource=False, default=False, ), Permission( name="annotate-rows", abbr=None, description="Can annotate rows", takes_database=True, takes_resource=True, default=False, ), Permission( name="view-debug-info", abbr=None, description="Can view debug information", takes_database=False, takes_resource=False, default=False, ), ] The new Action does not have a default= parameter. Here's the equivalent new code: from datasette import hookimpl from datasette.permissions import Action from datasette.resources import DatabaseResource, TableResource @hookimpl def register_actions(datasette): return [ Action( name="explain-sql", description="Explain SQL queries", resource_class=DatabaseResource, ), Action( name="annotate-rows", description="Annotate rows", resource_class=TableResource, ), Action( name="view-debug-info", description="View debug information", ), ] The abbr= is now optional and defaults to None . For actions that apply to specific resources (like databases or tables), specify the resource_class instead of takes_parent and takes_child . Note that view-debug-info does not specify a resource_class because it applies globally. | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade_guide:root-enabled-instances-during-testing | upgrade_guide | root-enabled-instances-during-testing | Root-enabled instances during testing | When writing tests that exercise root-only functionality, make sure to set datasette.root_enabled = True on the Datasette instance. Root permissions are only granted automatically when Datasette is started with datasette --root or when the flag is enabled directly in tests. | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide", "Root user checks are no longer necessary"] | [] |
| upgrade_guide:root-user-checks-are-no-longer-necessary | upgrade_guide | root-user-checks-are-no-longer-necessary | Root user checks are no longer necessary | Some plugins would introduce their own custom permission and then ensure the "root" actor had access to it using a pattern like this: @hookimpl def register_permissions(datasette): return [ Permission( name="upload-dbs", abbr=None, description="Upload SQLite database files", takes_database=False, takes_resource=False, default=False, ) ] @hookimpl def permission_allowed(actor, action): if action == "upload-dbs" and actor and actor.get("id") == "root": return True This is no longer necessary in Datasette 1.0a20 - the "root" actor automatically has all permissions when Datasette is started with the datasette --root option. The permission_allowed() hook in this example can be entirely removed. | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade_guide:target-the-new-apis-exclusively | upgrade_guide | target-the-new-apis-exclusively | Target the new APIs exclusively | Datasette 1.0a20’s permission system is substantially different from previous releases. Attempting to keep plugin code compatible with both the old permission_allowed() and the new allowed() interfaces leads to brittle workarounds. Prefer to adopt the 1.0a20 APIs ( register_actions , permission_resources_sql() , and datasette.allowed() ) outright and drop legacy fallbacks. | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade_guide:update-datasette-metadata-calls | upgrade_guide | update-datasette-metadata-calls | Update datasette.metadata() calls | The datasette.metadata() method has been removed. Use these methods instead: Old code: try: title = datasette.metadata(database=database)["queries"][query_name]["title"] except (KeyError, TypeError): pass New code: try: query_info = await datasette.get_canned_query(database, query_name, request.actor) if query_info and "title" in query_info: title = query_info["title"] except (KeyError, TypeError): pass | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade_guide:update-query-urls-in-tests | upgrade_guide | update-query-urls-in-tests | Update query URLs in tests | Datasette now redirects ?sql= parameters from database pages to the query view: Old code: response = await ds.client.get("/_memory.atom?sql=select+1") New code: response = await ds.client.get("/_memory/-/query.atom?sql=select+1") | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade_guide:update-render-functions-to-async | upgrade_guide | update-render-functions-to-async | Update render functions to async | If your plugin's render function needs to call datasette.get_canned_query() or other async Datasette methods, it must be declared as async: Old code: def render_atom(datasette, request, sql, columns, rows, database, table, query_name, view_name, data): # ... if query_name: title = datasette.metadata(database=database)["queries"][query_name]["title"] New code: async def render_atom(datasette, request, sql, columns, rows, database, table, query_name, view_name, data): # ... if query_name: query_info = await datasette.get_canned_query(database, query_name, request.actor) if query_info and "title" in query_info: title = query_info["title"] | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade_guide:update-test-constructors | upgrade_guide | update-test-constructors | Update test constructors | Old code: ds = Datasette( memory=True, metadata={ "databases": { "_memory": {"queries": {"my_query": {"sql": "select 1", "title": "My Query"}}} }, "plugins": { "my-plugin": {"setting": "value"} } } ) New code: ds = Datasette( memory=True, config={ "databases": { "_memory": {"queries": {"my_query": {"sql": "select 1", "title": "My Query"}}} }, "plugins": { "my-plugin": {"setting": "value"} } } ) | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide", "Migrating from metadata= to config="] | [] |
| upgrade_guide:upgrade-guide-v1-a25 | upgrade_guide | upgrade-guide-v1-a25 | Datasette 1.0a25: | datasette.create_token() is now an async method (previously it was synchronous). The restrict_all , restrict_database , and restrict_resource keyword arguments have been replaced by a single restrictions parameter that accepts a TokenRestrictions object. Old code: token = datasette.create_token( actor_id="user1", restrict_all=["view-instance", "view-table"], restrict_database={"docs": ["view-query"]}, restrict_resource={ "docs": { "attachments": ["insert-row", "update-row"] } }, ) New code: from datasette.tokens import TokenRestrictions token = await datasette.create_token( actor_id="user1", restrictions=( TokenRestrictions() .allow_all("view-instance") .allow_all("view-table") .allow_database("docs", "view-query") .allow_resource("docs", "attachments", "insert-row") .allow_resource("docs", "attachments", "update-row") ), ) The datasette create-token CLI command is unchanged. | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide"] | [] |
| upgrade_guide:using-datasette-allowed-to-check-permissions-instead-of-datasette-permission-allowed | upgrade_guide | using-datasette-allowed-to-check-permissions-instead-of-datasette-permission-allowed | Using datasette.allowed() to check permissions instead of datasette.permission_allowed() | The internal method datasette.permission_allowed() has been replaced by datasette.allowed() . The old method looked like this: can_debug = await datasette.permission_allowed( request.actor, "view-debug-info", ) can_explain_sql = await datasette.permission_allowed( request.actor, "explain-sql", resource="database_name", ) can_annotate_rows = await datasette.permission_allowed( request.actor, "annotate-rows", resource=(database_name, table_name), ) Note the confusing design here where resource could be either a string or a tuple depending on the permission being checked. The new keyword-only design makes this a lot more clear: from datasette.resources import DatabaseResource, TableResource can_debug = await datasette.allowed( actor=request.actor, action="view-debug-info", ) can_explain_sql = await datasette.allowed( actor=request.actor, action="explain-sql", resource=DatabaseResource(database_name), ) can_annotate_rows = await datasette.allowed( actor=request.actor, action="annotate-rows", resource=TableResource(database_name, table_name), ) | ["Upgrade guide", "Datasette 1.0a20 plugin upgrade guide"] | [] |
Advanced export
JSON shape: default, array, newline-delimited, object
CREATE TABLE [sections] ( [id] TEXT PRIMARY KEY, [page] TEXT, [ref] TEXT, [title] TEXT, [content] TEXT, [breadcrumbs] TEXT, [references] TEXT );