{"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1331089156", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1331089156, "node_id": "IC_kwDOBm6k_c5PVs8E", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-29T18:08:53Z", "updated_at": "2022-11-29T18:08:53Z", "author_association": "OWNER", "body": "I do think this needs type checking - I just tried and you really can send a string to an integer column and have it work, which feels bad.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1330974099", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1330974099, "node_id": "IC_kwDOBm6k_c5PVQ2T", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-29T17:03:00Z", "updated_at": "2022-11-29T17:11:05Z", "author_association": "OWNER", "body": "I've decided that I won't do that validation for the first version of this - I'm going to teach `dclient` to send the correct types instead: https://github.com/simonw/dclient/issues/6#issuecomment-1330963953", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1324539030", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1324539030, "node_id": "IC_kwDOBm6k_c5O8tyW", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-23T04:35:14Z", "updated_at": "2022-11-23T04:35:14Z", "author_association": "OWNER", "body": "If I do that I should probably update `insert` to do those validation checks as well.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1324531750", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1324531750, "node_id": "IC_kwDOBm6k_c5O8sAm", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-23T04:20:47Z", "updated_at": "2022-11-23T04:20:47Z", "author_association": "OWNER", "body": "... which does imply that I'm going to do an extra layer of validation over what SQLite provides. SQLite will happily allow a text string to be added to a supposedly integer column. I'm not going to allow that - I'll return a validation error instead, unless the string can be safely coerced to the correct type.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1324531085", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1324531085, "node_id": "IC_kwDOBm6k_c5O8r2N", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-23T04:19:28Z", "updated_at": "2022-11-23T04:19:28Z", "author_association": "OWNER", "body": "Had a design conversation with myself in https://github.com/simonw/dclient/issues/6 where I decided that the API should allow string values to be sent to integer columns which would be automatically converted *if possible to do so* - as an API usability feature.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1320563197", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1320563197, "node_id": "IC_kwDOBm6k_c5OtjH9", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-18T21:47:35Z", "updated_at": "2022-11-18T21:48:07Z", "author_association": "OWNER", "body": "Incomplete implementation of this view:\r\n```python\r\nclass RowUpdateView(BaseView):\r\n name = \"row-update\"\r\n\r\n def __init__(self, datasette):\r\n self.ds = datasette\r\n\r\n async def post(self, request):\r\n database_route = tilde_decode(request.url_vars[\"database\"])\r\n table = tilde_decode(request.url_vars[\"table\"])\r\n try:\r\n db = self.ds.get_database(route=database_route)\r\n except KeyError:\r\n return _error([\"Database not found: {}\".format(database_route)], 404)\r\n\r\n database_name = db.name\r\n if not await db.table_exists(table):\r\n return _error([\"Table not found: {}\".format(table)], 404)\r\n\r\n pk_values = urlsafe_components(request.url_vars[\"pks\"])\r\n\r\n sql, params, pks = await row_sql_params_pks(db, table, pk_values)\r\n results = await db.execute(sql, params, truncate=True)\r\n rows = list(results.rows)\r\n if not rows:\r\n return _error([f\"Record not found: {pk_values}\"], 404)\r\n\r\n # Ensure user has permission to update this row\r\n if not await self.ds.permission_allowed(\r\n request.actor, \"update-row\", resource=(database_name, table)\r\n ):\r\n return _error([\"Permission denied\"], 403)\r\n\r\n body = await request.post_body()\r\n try:\r\n data = json.loads(body)\r\n except json.JSONDecodeError as e:\r\n return _error([\"Invalid JSON: {}\".format(e)])\r\n if not isinstance(data, dict):\r\n return _error([\"JSON must be a dictionary\"])\r\n\r\n def update_row(conn):\r\n sqlite_utils.Database(conn)[table].update(pk_values, updates)\r\n\r\n await db.execute_write_fn(update_row)\r\n result = {\"ok\": True}\r\n if data.get(\"return\"):\r\n result[\"row\"] = {\"row-here\": \"TODO\"}\r\n return Response.json(result, status=200)\r\n```\r\nThis is before the refactor in:\r\n- #1896", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1317755263", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1317755263, "node_id": "IC_kwDOBm6k_c5Oi1l_", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-16T22:24:59Z", "updated_at": "2022-11-16T22:24:59Z", "author_association": "OWNER", "body": "In trying to write this I realize that there's a lot of duplicated code with delete row, specifically around resolving the incoming URL into a row (or a database or a table).\r\n\r\nSince this is so common, I think it's worth extracting the logic out first.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1315812212", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1315812212, "node_id": "IC_kwDOBm6k_c5ObbN0", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-15T20:12:02Z", "updated_at": "2022-11-15T20:12:02Z", "author_association": "OWNER", "body": "If the update succeeds it will return `{\"ok\": true}`.\r\n\r\nFor consistency with `/db/table/-/insert` you can pass `\"return\": true` and it will return a `\"row\"` key with the now-updated full row.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1315809867", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1315809867, "node_id": "IC_kwDOBm6k_c5ObapL", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-15T20:09:44Z", "updated_at": "2022-11-15T20:09:44Z", "author_association": "OWNER", "body": "I'm also not going to implement `\"alter\": true` yet (which would add any missing columns based on the update) - I'll hold that off for a later feature.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1315809260", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1315809260, "node_id": "IC_kwDOBm6k_c5Obafs", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-15T20:09:11Z", "updated_at": "2022-11-15T20:09:11Z", "author_association": "OWNER", "body": "I'm going to use the error format I've been experimenting with here:\r\n- #1875\r\n\r\n```json\r\n{\r\n \"type\": \"https://example.net/validation-error\",\r\n \"title\": \"Your request is not valid.\",\r\n \"errors\": [\r\n {\r\n \"detail\": \"must be a positive integer\",\r\n \"pointer\": \"#/age\"\r\n },\r\n {\r\n \"detail\": \"must be 'green', 'red' or 'blue'\",\r\n \"pointer\": \"#/profile/color\"\r\n }\r\n ]\r\n}\r\n```\r\nI'm not quite ready to commit to a `type` URL though, so I'll leave that to be solved later should I fully embrace that RFC.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1315808062", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1315808062, "node_id": "IC_kwDOBm6k_c5ObaM-", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-15T20:08:04Z", "updated_at": "2022-11-15T20:08:04Z", "author_association": "OWNER", "body": "The initial design I'm going to implement will look like this:\r\n\r\n```\r\nPOST /db/table/1/-/update\r\nAuthorization: Bearer xxx\r\nContent-Type: application/json\r\n```\r\n```json\r\n{\r\n \"update\": {\r\n \"name\": \"New name\"\r\n }\r\n}\r\n```\r\nAny fields that are not yet columns will return an error.\r\n\r\nShould it enforce types, in as much as an integer column should have a JSON integer passed to it, or should it allow strings containing valid integers?\r\n\r\nI'm going to allow strings, mainly as a workaround for the fact that JavaScript integers have a maximum size.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1302790013", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1302790013, "node_id": "IC_kwDOBm6k_c5Npv99", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-03T23:32:30Z", "updated_at": "2022-11-03T23:32:30Z", "author_association": "OWNER", "body": "I'm not going to allow updates to primary keys. If you need to do that, you can instead delete the record and then insert a new one with the new primary keys you wanted - or maybe use a custom SQL query.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1302785086", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1302785086, "node_id": "IC_kwDOBm6k_c5Npuw-", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-03T23:24:33Z", "updated_at": "2022-11-03T23:24:56Z", "author_association": "OWNER", "body": "Thinking more about validation: I'm considering if this should validate that columns which are defined as SQLite foreign keys are being updated to values that exist in those other tables.\r\n\r\nI like the sound of this. It seems like a sensible default behaviour for Datasette. And it fits with the fact that Datasette treats foreign keys specially elsewhere in the interface.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1302760549", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1302760549, "node_id": "IC_kwDOBm6k_c5Npoxl", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-03T22:43:04Z", "updated_at": "2022-11-03T23:21:31Z", "author_association": "OWNER", "body": "The `id=(int, ...)` thing is weird, but is apparently Pydantic syntax for a required field?\r\n\r\nhttps://cs.github.com/starlite-api/starlite/blob/28ddc847c4cb072f0d5d21a9ecd5259711f12ec9/docs/usage/11-data-transfer-objects.md#L161 confirms:\r\n\r\n> 1. For required fields use a tuple of type + ellipsis, for example `(str, ...)`.\r\n> 2. For optional fields use a tuple of type + `None`, for example `(str, None)`\r\n> 3. To set a default value use a tuple of type + default value, for example `(str, \"Hello World\")`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1302760382", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1302760382, "node_id": "IC_kwDOBm6k_c5Npou-", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-03T22:42:47Z", "updated_at": "2022-11-03T22:42:47Z", "author_association": "OWNER", "body": "```python\r\nprint(create_model('document', id=(int, ...), title=(str, None)).schema_json(indent=2))\r\n```\r\n```json\r\n{\r\n \"title\": \"document\",\r\n \"type\": \"object\",\r\n \"properties\": {\r\n \"id\": {\r\n \"title\": \"Id\",\r\n \"type\": \"integer\"\r\n },\r\n \"title\": {\r\n \"title\": \"Title\",\r\n \"type\": \"string\"\r\n }\r\n },\r\n \"required\": [\r\n \"id\"\r\n ]\r\n}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1863#issuecomment-1302759174", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1863", "id": 1302759174, "node_id": "IC_kwDOBm6k_c5NpocG", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-03T22:40:47Z", "updated_at": "2022-11-03T22:40:47Z", "author_association": "OWNER", "body": "I'm considering Pydantic for this, see:\r\n- https://github.com/simonw/datasette/issues/1882#issuecomment-1302716350\r\n\r\nIn particular the `create_model()` method: https://pydantic-docs.helpmanual.io/usage/models/#dynamic-model-creation\r\n\r\nThis would give me good validation. It would also, weirdly, give me the ability to output JSON schema. Maybe I could have this as the JSON schema for a row?\r\n\r\n`/db/table/-/json-schema`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1425029242, "label": "Update a single record in an existing table"}, "performed_via_github_app": null}