From 5201e172ec2cb25799807e9727ec0b04e69c9f8d Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Fri, 7 Nov 2025 16:41:51 -0800 Subject: [PATCH 1/3] Applied Black --- datasette_write_ui/__init__.py | 3 +-- datasette_write_ui/routes.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/datasette_write_ui/__init__.py b/datasette_write_ui/__init__.py index ff2ad15..1e4bc2e 100644 --- a/datasette_write_ui/__init__.py +++ b/datasette_write_ui/__init__.py @@ -9,11 +9,10 @@ def register_routes(): (r"^/-/datasette-write-ui/insert-row-details$", routes.insert_row_details), ] + @hookimpl def extra_template_vars(datasette, database, table): async def permission_allowed(actor, permission): return await datasette.permission_allowed(actor, permission, (database, table)) return {"permission_allowed": permission_allowed} - - diff --git a/datasette_write_ui/routes.py b/datasette_write_ui/routes.py index d94754b..d852cea 100644 --- a/datasette_write_ui/routes.py +++ b/datasette_write_ui/routes.py @@ -2,6 +2,7 @@ from datasette.utils import escape_sqlite, tilde_decode from typing import Any, TypedDict + def affinity_from_type(type): """ Return the "affinity" a SQLite column type has. @@ -23,8 +24,6 @@ def affinity_from_type(type): return "numeric" - - class EditRowDetailsField(TypedDict): """ Each "field" returned for every editable column in the edit-row-details route. @@ -36,6 +35,7 @@ class EditRowDetailsField(TypedDict): pk: bool editable: bool + async def edit_row_details(scope, receive, datasette, request): db_name = request.args.get("db") table_name = request.args.get("table") From e327070a08b4d6f6da6f42fc86e993cce808139f Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Fri, 7 Nov 2025 16:58:20 -0800 Subject: [PATCH 2/3] datasette-write-ui: Upgrade for Datasette 1.0a21 Refshttps://github.com/simonw/datasette/issues/2577 --- datasette_write_ui/__init__.py | 9 +++++++- datasette_write_ui/routes.py | 39 ++++++++++++++++++++++---------- pyproject.toml | 6 ++--- tests/test_datasette_write_ui.py | 7 +++++- 4 files changed, 44 insertions(+), 17 deletions(-) diff --git a/datasette_write_ui/__init__.py b/datasette_write_ui/__init__.py index 1e4bc2e..1c184cd 100644 --- a/datasette_write_ui/__init__.py +++ b/datasette_write_ui/__init__.py @@ -1,4 +1,5 @@ from datasette import hookimpl +from datasette.resources import TableResource from . import routes @@ -12,7 +13,13 @@ def register_routes(): @hookimpl def extra_template_vars(datasette, database, table): + table_resource = TableResource(database=database, table=table) + async def permission_allowed(actor, permission): - return await datasette.permission_allowed(actor, permission, (database, table)) + return await datasette.allowed( + actor=actor, + action=permission, + resource=table_resource, + ) return {"permission_allowed": permission_allowed} diff --git a/datasette_write_ui/routes.py b/datasette_write_ui/routes.py index d852cea..a0edf19 100644 --- a/datasette_write_ui/routes.py +++ b/datasette_write_ui/routes.py @@ -1,4 +1,5 @@ from datasette import Response, Forbidden +from datasette.resources import TableResource from datasette.utils import escape_sqlite, tilde_decode from typing import Any, TypedDict @@ -41,11 +42,6 @@ async def edit_row_details(scope, receive, datasette, request): table_name = request.args.get("table") pks = request.args.get("primaryKeys") - if not await datasette.permission_allowed( - request.actor, "update-row", (db_name, table_name), default=False - ): - raise Forbidden("update-row permissions required") - if db_name is None: return Response.json( {"ok": False, "message": "db parameter is required"}, status=400 @@ -56,6 +52,15 @@ async def edit_row_details(scope, receive, datasette, request): {"ok": False, "message": "table parameter is required"}, status=400 ) + table_resource = TableResource(database=db_name, table=table_name) + + if not await datasette.allowed( + actor=request.actor, + action="update-row", + resource=table_resource, + ): + raise Forbidden("update-row permissions required") + if pks is None: return Response.json( {"ok": False, "message": "primaryKeys parameter is required"}, status=400 @@ -134,8 +139,22 @@ async def insert_row_details(scope, receive, datasette, request): db_name = request.args.get("db") table_name = request.args.get("table") - if not await datasette.permission_allowed( - request.actor, "insert-row", (db_name, table_name) + if db_name is None: + return Response.json( + {"ok": False, "message": "db parameter is required"}, status=400 + ) + + if table_name is None: + return Response.json( + {"ok": False, "message": "table parameter is required"}, status=400 + ) + + table_resource = TableResource(database=db_name, table=table_name) + + if not await datasette.allowed( + actor=request.actor, + action="insert-row", + resource=table_resource, ): raise Forbidden("insert-row permissions required") @@ -151,8 +170,4 @@ async def insert_row_details(scope, receive, datasette, request): InsertRowDetailField(name=name, affinity=affinity_from_type(type)) ) - return Response.json( - { - "fields": insertable_columns, - } - ) + return Response.json({"fields": insertable_columns}) diff --git a/pyproject.toml b/pyproject.toml index 438f64d..17001f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,13 +4,13 @@ version = "0.0.1a13" description = "Datasette UI plugin for editing and inserting rows" readme = "README.md" authors = [{name = "Alex Garcia"}] -license = {file = "LICENSE"} +license = "Apache-2.0" classifiers=[ "Framework :: Datasette", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ - "datasette==1.0a19", + "datasette>=1.0a21", "sqlite-utils>=3.38", ] diff --git a/tests/test_datasette_write_ui.py b/tests/test_datasette_write_ui.py index 5fc3315..bb8d231 100644 --- a/tests/test_datasette_write_ui.py +++ b/tests/test_datasette_write_ui.py @@ -39,7 +39,9 @@ def students_db_path(tmpdir): students_metadata = { "databases": { "students": { - "tables": {"students": {"permissions": {"insert-row": {"id": "apollo"}}}} + "tables": { + "students": {"permissions": {"insert-row": {"id": ["apollo", "root"]}}} + } } } } @@ -63,6 +65,7 @@ async def test_permissions(students_db_path): [students_db_path], config=students_metadata, ) + datasette.root_enabled = True response = await datasette.client.get("/students/students") permissions = get_permission_from_table_html(response.text) assert permissions["can_delete"] == False @@ -100,6 +103,7 @@ async def test_permissions(students_db_path): @pytest.mark.asyncio async def test_insert_row_details_route(students_db_path): datasette = Datasette([students_db_path]) + datasette.root_enabled = True response = await datasette.client.get( "/-/datasette-write-ui/insert-row-details?db=students&table=students", @@ -123,6 +127,7 @@ async def test_insert_row_details_route(students_db_path): @pytest.mark.asyncio async def test_update_row_details_route(students_db_path): datasette = Datasette([students_db_path]) + datasette.root_enabled = True response = await datasette.client.get( "/-/datasette-write-ui/edit-row-details?db=students&table=students", From 300a9e08fbee0ff91fbbcf3ec160afd126c981e6 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Fri, 7 Nov 2025 17:00:27 -0800 Subject: [PATCH 3/3] Remove some variables --- datasette_write_ui/__init__.py | 4 +--- datasette_write_ui/routes.py | 8 ++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/datasette_write_ui/__init__.py b/datasette_write_ui/__init__.py index 1c184cd..14725ab 100644 --- a/datasette_write_ui/__init__.py +++ b/datasette_write_ui/__init__.py @@ -13,13 +13,11 @@ def register_routes(): @hookimpl def extra_template_vars(datasette, database, table): - table_resource = TableResource(database=database, table=table) - async def permission_allowed(actor, permission): return await datasette.allowed( actor=actor, action=permission, - resource=table_resource, + resource=TableResource(database=database, table=table), ) return {"permission_allowed": permission_allowed} diff --git a/datasette_write_ui/routes.py b/datasette_write_ui/routes.py index a0edf19..c5cb77b 100644 --- a/datasette_write_ui/routes.py +++ b/datasette_write_ui/routes.py @@ -52,12 +52,10 @@ async def edit_row_details(scope, receive, datasette, request): {"ok": False, "message": "table parameter is required"}, status=400 ) - table_resource = TableResource(database=db_name, table=table_name) - if not await datasette.allowed( actor=request.actor, action="update-row", - resource=table_resource, + resource=TableResource(database=db_name, table=table_name), ): raise Forbidden("update-row permissions required") @@ -149,12 +147,10 @@ async def insert_row_details(scope, receive, datasette, request): {"ok": False, "message": "table parameter is required"}, status=400 ) - table_resource = TableResource(database=db_name, table=table_name) - if not await datasette.allowed( actor=request.actor, action="insert-row", - resource=table_resource, + resource=TableResource(database=db_name, table=table_name), ): raise Forbidden("insert-row permissions required")