From 6541ce633ebf56770106221b0984751270adb2c7 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 7 Jul 2018 22:21:51 -0700 Subject: [PATCH] Fix for row pages for tables with / in, closes #325 --- datasette/views/base.py | 13 ++++++++++++- datasette/views/table.py | 4 +++- tests/test_api.py | 6 ++++++ tests/test_html.py | 13 +++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/datasette/views/base.py b/datasette/views/base.py index 6d6bf0ac..4484ecf7 100644 --- a/datasette/views/base.py +++ b/datasette/views/base.py @@ -122,9 +122,16 @@ class BaseView(RenderMixin): kwargs["table"] = table if _format: kwargs["as_format"] = ".{}".format(_format) + elif "table" in kwargs: + kwargs["table"] = urllib.parse.unquote_plus( + kwargs["table"] + ) + should_redirect = "/{}-{}".format(name, expected) if "table" in kwargs: - should_redirect += "/" + urllib.parse.quote_plus(kwargs["table"]) + should_redirect += "/" + urllib.parse.quote_plus( + kwargs["table"] + ) if "pk_path" in kwargs: should_redirect += "/" + kwargs["pk_path"] if "as_format" in kwargs: @@ -253,6 +260,10 @@ class BaseView(RenderMixin): _format = _format or _ext_format kwargs["table"] = table del kwargs["table_and_format"] + elif "table" in kwargs: + kwargs["table"] = urllib.parse.unquote_plus( + kwargs["table"] + ) if _format == "csv": return await self.as_csv(request, name, hash, **kwargs) diff --git a/datasette/views/table.py b/datasette/views/table.py index cecdbb4a..843960b9 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -806,7 +806,9 @@ class RowView(RowTableShared): select = "rowid, *" pks = ["rowid"] wheres = ['"{}"=:p{}'.format(pk, i) for i, pk in enumerate(pks)] - sql = 'select {} from "{}" where {}'.format(select, table, " AND ".join(wheres)) + sql = 'select {} from {} where {}'.format( + select, escape_sqlite(table), " AND ".join(wheres) + ) params = {} for i, pk_value in enumerate(pk_values): params["p{}".format(i)] = pk_value diff --git a/tests/test_api.py b/tests/test_api.py index 61987f0a..bc20c6dd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -865,6 +865,12 @@ def test_row(app_client): assert [{'id': '1', 'content': 'hello'}] == response.json['rows'] +def test_row_strange_table_name(app_client): + response = app_client.get('/fixtures/table%2Fwith%2Fslashes.csv/3.json?_shape=objects') + assert response.status == 200 + assert [{'pk': '3', 'content': 'hey'}] == response.json['rows'] + + def test_row_foreign_key_tables(app_client): response = app_client.get('/fixtures/simple_primary_key/1.json?_extras=foreign_key_tables') assert response.status == 200 diff --git a/tests/test_html.py b/tests/test_html.py index ee9d3c41..41a8bab6 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -56,6 +56,19 @@ def test_row(app_client): assert response.status == 200 +def test_row_strange_table_name(app_client): + response = app_client.get( + '/fixtures/table%2Fwith%2Fslashes.csv/3', + allow_redirects=False + ) + assert response.status == 302 + assert response.headers['Location'].endswith( + '/table%2Fwith%2Fslashes.csv/3' + ) + response = app_client.get('/fixtures/table%2Fwith%2Fslashes.csv/3') + assert response.status == 200 + + def test_add_filter_redirects(app_client): filter_args = urllib.parse.urlencode({ '_filter_column': 'content',