diff --git a/datasette/views/database.py b/datasette/views/database.py
index bdd433cc..9a8aca32 100644
--- a/datasette/views/database.py
+++ b/datasette/views/database.py
@@ -11,6 +11,7 @@ from datasette.utils import (
add_cors_headers,
await_me_maybe,
derive_named_parameters,
+ format_bytes,
tilde_decode,
to_css_class,
validate_sql_select,
@@ -399,13 +400,18 @@ class QueryView(DataView):
).hexdigest(),
},
)
- display_value = Markup(
- '<Binary: {} byte{}>'.format(
+ formatted = format_bytes(len(value))
+ display_value = markupsafe.Markup(
+ '<Binary: {:,} byte{}>'.format(
blob_url,
- len(display_value),
+ ' title="{}"'.format(formatted)
+ if "bytes" not in formatted
+ else "",
+ len(value),
"" if len(value) == 1 else "s",
)
)
+
display_row.append(display_value)
display_rows.append(display_row)
diff --git a/datasette/views/table.py b/datasette/views/table.py
index cd7afea6..dc85165e 100644
--- a/datasette/views/table.py
+++ b/datasette/views/table.py
@@ -12,10 +12,12 @@ from datasette.utils import (
MultiParams,
append_querystring,
compound_keys_after_sql,
+ format_bytes,
tilde_decode,
tilde_encode,
escape_sqlite,
filters_should_redirect,
+ format_bytes,
is_url,
path_from_row_pks,
path_with_added_args,
@@ -175,14 +177,18 @@ class RowTableShared(DataView):
if plugin_display_value:
display_value = plugin_display_value
elif isinstance(value, bytes):
+ formatted = format_bytes(len(value))
display_value = markupsafe.Markup(
- '<Binary: {} byte{}>'.format(
+ '<Binary: {:,} byte{}>'.format(
self.ds.urls.row_blob(
database,
table,
path_from_row_pks(row, pks, not pks),
column,
),
+ ' title="{}"'.format(formatted)
+ if "bytes" not in formatted
+ else "",
len(value),
"" if len(value) == 1 else "s",
)
diff --git a/tests/test_table_html.py b/tests/test_table_html.py
index 6dc26434..d3cb3e17 100644
--- a/tests/test_table_html.py
+++ b/tests/test_table_html.py
@@ -1,3 +1,4 @@
+from datasette.app import Datasette, Database
from bs4 import BeautifulSoup as Soup
from .fixtures import ( # noqa
app_client,
@@ -1089,3 +1090,28 @@ def test_allow_facet_off(allow_facet):
assert "Suggested facets" in response.text
else:
assert "Suggested facets" not in response.text
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+ "size,title,length_bytes",
+ (
+ (2000, ' title="2.0 KB"', "2,000"),
+ (20000, ' title="19.5 KB"', "20,000"),
+ (20, "", "20"),
+ ),
+)
+async def test_format_of_binary_links(size, title, length_bytes):
+ ds = Datasette()
+ db_name = "binary-links-{}".format(size)
+ db = ds.add_memory_database(db_name)
+ sql = "select zeroblob({}) as blob".format(size)
+ await db.execute_write("create table blobs as {}".format(sql))
+ response = await ds.client.get("/{}/blobs".format(db_name))
+ assert response.status_code == 200
+ expected = "{}><Binary: {} bytes>".format(title, length_bytes)
+ assert expected in response.text
+ # And test with arbitrary SQL query too
+ sql_response = await ds.client.get("/{}".format(db_name), params={"sql": sql})
+ assert sql_response.status_code == 200
+ assert expected in sql_response.text