mirror of
https://github.com/simonw/datasette.git
synced 2026-10-18 15:26:57 +02:00
Merge 27424e8981 into c435a80030
This commit is contained in:
commit
e42a981d5d
2 changed files with 90 additions and 5 deletions
|
|
@ -721,6 +721,8 @@ async def display_columns_and_rows(
|
|||
# Unless we are a view, the first column is a link - either to the rowid
|
||||
# or to the simple or compound primary key
|
||||
if link_column:
|
||||
# NULL keys may be duplicated and encode like a real string key.
|
||||
has_null_pk = bool(pks) and any(row[pk] is None for pk in pks)
|
||||
is_special_link_column = len(pks) != 1
|
||||
pk_path = path_from_row_pks(row, pks, not pks, False)
|
||||
row_path = path_from_row_pks(row, pks, not pks)
|
||||
|
|
@ -729,9 +731,9 @@ async def display_columns_and_rows(
|
|||
if row_label and row_label != pk_path:
|
||||
row_action_label = f"{pk_path} {row_label}"
|
||||
table_path = datasette.urls.table(database_name, table_name)
|
||||
row_link = (
|
||||
f'<a href="{table_path}/{row_path}">{markupsafe.escape(pk_path)!s}</a>'
|
||||
)
|
||||
row_link = markupsafe.escape(pk_path)
|
||||
if not has_null_pk:
|
||||
row_link = f'<a href="{table_path}/{row_path}">{markupsafe.escape(pk_path)!s}</a>'
|
||||
edit_icon = (
|
||||
'<svg class="row-inline-action-icon" aria-hidden="true" '
|
||||
'xmlns="http://www.w3.org/2000/svg" width="14" height="14" '
|
||||
|
|
@ -754,14 +756,14 @@ async def display_columns_and_rows(
|
|||
"</svg>"
|
||||
)
|
||||
row_actions = []
|
||||
if row_action_permissions.get("update-row"):
|
||||
if not has_null_pk and row_action_permissions.get("update-row"):
|
||||
row_actions.append(
|
||||
'<button type="button" class="row-inline-action row-inline-action-edit" '
|
||||
f'aria-label="Edit row {markupsafe.escape(row_action_label)}" title="Edit row" '
|
||||
'data-row-action="edit">'
|
||||
f"{edit_icon}</button>"
|
||||
)
|
||||
if row_action_permissions.get("delete-row"):
|
||||
if not has_null_pk and row_action_permissions.get("delete-row"):
|
||||
row_actions.append(
|
||||
'<button type="button" class="row-inline-action row-inline-action-delete" '
|
||||
f'aria-label="Delete row {markupsafe.escape(row_action_label)}" title="Delete row" '
|
||||
|
|
|
|||
|
|
@ -1087,6 +1087,89 @@ async def test_row_delete_action_data_attributes():
|
|||
ds.close()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"schema_and_rows, link_column, valid_row_path",
|
||||
[
|
||||
(
|
||||
"""
|
||||
create table items (id text primary key, name text);
|
||||
insert into items (id, name) values
|
||||
(null, 'Null key'),
|
||||
('None', 'Literal key');
|
||||
""",
|
||||
"id",
|
||||
"/data/items/None",
|
||||
),
|
||||
(
|
||||
"""
|
||||
create table items (
|
||||
namespace text,
|
||||
id text,
|
||||
name text,
|
||||
primary key (namespace, id)
|
||||
);
|
||||
insert into items (namespace, id, name) values
|
||||
(null, 'item', 'Null key'),
|
||||
('None', 'item', 'Literal key');
|
||||
""",
|
||||
"Link",
|
||||
"/data/items/None,item",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_null_primary_key_rows_have_no_row_link_or_actions(
|
||||
schema_and_rows, link_column, valid_row_path
|
||||
):
|
||||
ds = Datasette(
|
||||
[],
|
||||
config={
|
||||
"databases": {
|
||||
"data": {
|
||||
"tables": {
|
||||
"items": {
|
||||
"permissions": {
|
||||
"update-row": {"id": "root"},
|
||||
"delete-row": {"id": "root"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
try:
|
||||
db = ds.add_database(
|
||||
Database(ds, memory_name="test_null_primary_key_rows"), name="data"
|
||||
)
|
||||
await db.execute_write_script(schema_and_rows)
|
||||
|
||||
response = await ds.client.get("/data/items", actor={"id": "root"})
|
||||
assert response.status_code == 200
|
||||
soup = Soup(response.text, "html.parser")
|
||||
rows = soup.select("table.rows-and-columns tbody tr")
|
||||
null_row = next(
|
||||
row
|
||||
for row in rows
|
||||
if row.select_one(".col-name").get_text(strip=True) == "Null key"
|
||||
)
|
||||
literal_row = next(
|
||||
row
|
||||
for row in rows
|
||||
if row.select_one(".col-name").get_text(strip=True) == "Literal key"
|
||||
)
|
||||
|
||||
assert null_row.select_one(f'td[class~="col-{link_column}"] a') is None
|
||||
assert null_row.select("button[data-row-action]") == []
|
||||
|
||||
literal_link = literal_row.select_one(f'td[class~="col-{link_column}"] a')
|
||||
assert literal_link is not None
|
||||
assert literal_link["href"] == valid_row_path
|
||||
assert len(literal_row.select("button[data-row-action]")) == 2
|
||||
finally:
|
||||
ds.close()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_database_create_table_action_button_and_data():
|
||||
ds = Datasette(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue