Row update return:true responds with rows list, matching insert/upsert

Row update previously returned a singular "row" object where insert and
upsert return a "rows" list. All write endpoints now use "rows".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GrHZSypDfMnym1tM5XJAFZ
This commit is contained in:
Claude 2026-07-04 16:03:05 +00:00
commit b09dceea88
No known key found for this signature in database
7 changed files with 40 additions and 13 deletions

View file

@ -828,7 +828,7 @@ class RowUpdateView(BaseView):
resolved.sql, resolved.params, truncate=True
)
returned_row = results.dicts()[0]
result["row"] = returned_row
result["rows"] = [returned_row]
await self.ds.track_event(
UpdateRowEvent(

View file

@ -1955,11 +1955,13 @@ The returned JSON will look like this:
{
"ok": true,
"row": {
"id": 1,
"title": "New title",
"other_column": "Will be present here too"
}
"rows": [
{
"id": 1,
"title": "New title",
"other_column": "Will be present here too"
}
]
}
Any errors will use the :ref:`standard error format <json_api_errors>`, with a ``400`` status code for a bad input or a ``403`` status code for an authentication or permission error.

View file

@ -958,8 +958,8 @@ does not change the SQLite schema.
`"Invalid keys: ..."`; write failures (bad column, constraint violation) →
400 with the message.
- **Response** — 200 `{"ok": true}`; with `return: true`,
`{"ok": true, "row": {...}}` (singular `row`, unlike insert/upsert's
`rows`). Emits `update-row`.
`{"ok": true, "rows": [{...}]}` — a single-item list, matching
insert/upsert. Emits `update-row`.
### POST /\<database\>/\<table\>/\<pks\>/-/delete

View file

@ -160,10 +160,11 @@ Endpoints disagree about the success envelope:
(index.py:147-161); `/-/databases.json` returns an **array**; the database
page returns `tables` as an array. Choose arrays-of-objects everywhere
(objects-keyed-by-name break when names need ordering or pagination).
- Insert/upsert with `return: true` respond with `rows` (plural, list); row
- ~~Insert/upsert with `return: true` respond with `rows` (plural, list); row
update with `return: true` responds with `row` (singular, object)
(views/row.py:837-844). Pick one (`rows` everywhere, even for one row,
matches the read API).
matches the read API).~~ ✅ **Done** — row update now returns
`rows: [{...}]`.
### 2b. `_extra`/`_shape` support is uneven (P2)

View file

@ -1481,9 +1481,9 @@ async def test_update_row(ds_write, input, expected_errors, use_return):
assert response.json()["ok"] is True
if not use_return:
assert "row" not in response.json()
assert "rows" not in response.json()
else:
returned_row = response.json()["row"]
returned_row = response.json()["rows"][0]
assert returned_row["id"] == pk
for k, v in input.items():
assert returned_row[k] == v

View file

@ -507,3 +507,27 @@ async def test_query_html_without_sql_is_still_the_editor(ds_client):
response = await ds_client.get("/fixtures/-/query")
assert response.status_code == 200
assert response.headers["content-type"].startswith("text/html")
# Write API return:true responses use "rows" consistently
@pytest.mark.asyncio
async def test_row_update_return_uses_rows_list(ds_error_shape):
await ds_error_shape.client.post(
"/data/docs/-/insert",
json={"row": {"id": 1, "title": "One"}},
headers={"Content-Type": "application/json"},
actor={"id": "root"},
)
response = await ds_error_shape.client.post(
"/data/docs/1/-/update",
json={"update": {"title": "Updated"}, "return": True},
headers={"Content-Type": "application/json"},
actor={"id": "root"},
)
assert response.status_code == 200
data = response.json()
assert data["ok"] is True
assert "row" not in data
assert data["rows"] == [{"id": 1, "title": "Updated"}]

View file

@ -1629,7 +1629,7 @@ async def test_row_update_sets_message():
json={"update": {"name": long_name}, "return": True},
)
assert response.status_code == 200
assert response.json()["row"]["name"] == long_name
assert response.json()["rows"][0]["name"] == long_name
assert ds.unsign(response.cookies["ds_messages"], "messages") == [
["Updated row 1 ({})".format(truncated_name), ds.INFO]
]