Custom SQL query{% if display_rows %} returning {% if truncated %}more than {% endif %}{{ "{:,}".format(display_rows|length) }} row{% if display_rows|length == 1 %}{% else %}s{% endif %}{% endif %}{% if not query_error %} {% if hide_sql %}(show){% else %}(hide){% endif %}{% endif %}
+
Custom SQL query{% if display_rows %} returning {% if truncated %}more than {% endif %}{{ "{:,}".format(display_rows|length) }} row{% if display_rows|length == 1 %}{% else %}s{% endif %}{% endif %}{% if not query_error %}
+ ({{ show_hide_text }})
+ {% endif %}
{% if error %}
{{ error }}
{% endif %}
@@ -44,8 +46,11 @@
Custom SQL query{% if display_rows %} returning {% if truncated %}more than
{% if query %}{{ query.sql }}{% endif %}
{% endif %}
{% else %}
- {% if not canned_query %}{% endif %}
-
+ {% if not canned_query %}
+
+ {% endif %}
{% endif %}
{% if named_parameter_values %}
Query parameters
@@ -54,9 +59,10 @@
Query parameters
{% endfor %}
{% endif %}
-
+ {% if not hide_sql %}{% endif %}
{% if canned_write %}{% endif %}
+ {{ show_hide_hidden }}
{% if canned_query and edit_sql_url %}Edit SQL{% endif %}
diff --git a/datasette/views/database.py b/datasette/views/database.py
index 53bdceed1b..d9fe2b4966 100644
--- a/datasette/views/database.py
+++ b/datasette/views/database.py
@@ -5,6 +5,8 @@
from markupsafe import Markup, escape
from urllib.parse import parse_qsl, urlencode
+import markupsafe
+
from datasette.utils import (
await_me_maybe,
check_visibility,
@@ -415,6 +417,29 @@ async def extra_template():
}
)
)
+
+ show_hide_hidden = ""
+ if metadata.get("hide_sql"):
+ if bool(params.get("_show_sql")):
+ show_hide_link = path_with_removed_args(request, {"_show_sql"})
+ show_hide_text = "hide"
+ show_hide_hidden = (
+ ''
+ )
+ else:
+ show_hide_link = path_with_added_args(request, {"_show_sql": 1})
+ show_hide_text = "show"
+ else:
+ if bool(params.get("_hide_sql")):
+ show_hide_link = path_with_removed_args(request, {"_hide_sql"})
+ show_hide_text = "show"
+ show_hide_hidden = (
+ ''
+ )
+ else:
+ show_hide_link = path_with_added_args(request, {"_hide_sql": 1})
+ show_hide_text = "hide"
+ hide_sql = show_hide_text == "show"
return {
"display_rows": display_rows,
"custom_sql": True,
@@ -425,9 +450,10 @@ async def extra_template():
"metadata": metadata,
"config": self.ds.config_dict(),
"request": request,
- "path_with_added_args": path_with_added_args,
- "path_with_removed_args": path_with_removed_args,
- "hide_sql": "_hide_sql" in params,
+ "show_hide_link": show_hide_link,
+ "show_hide_text": show_hide_text,
+ "show_hide_hidden": markupsafe.Markup(show_hide_hidden),
+ "hide_sql": hide_sql,
}
return (
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 883cb3eb3a..d0fee19b9f 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -674,7 +674,7 @@ The main focus of this release is a major upgrade to the :ref:`plugin_register_o
* Visually distinguish float and integer columns - useful for figuring out why order-by-column might be returning unexpected results. (:issue:`729`)
* The :ref:`internals_request`, which is passed to several plugin hooks, is now documented. (:issue:`706`)
* New ``metadata.json`` option for setting a custom default page size for specific tables and views, see :ref:`metadata_page_size`. (:issue:`751`)
-* Canned queries can now be configured with a default URL fragment hash, useful when working with plugins such as `datasette-vega `__, see :ref:`canned_queries_default_fragment`. (:issue:`706`)
+* Canned queries can now be configured with a default URL fragment hash, useful when working with plugins such as `datasette-vega `__, see :ref:`canned_queries_options`. (:issue:`706`)
* Fixed a bug in ``datasette publish`` when running on operating systems where the ``/tmp`` directory lives in a different volume, using a backport of the Python 3.8 ``shutil.copytree()`` function. (:issue:`744`)
* Every plugin hook is now covered by the unit tests, and a new unit test checks that each plugin hook has at least one corresponding test. (:issue:`771`, :issue:`773`)
diff --git a/docs/sql_queries.rst b/docs/sql_queries.rst
index 3049593df2..407e4ba2a3 100644
--- a/docs/sql_queries.rst
+++ b/docs/sql_queries.rst
@@ -187,14 +187,28 @@ You can alternatively provide an explicit list of named parameters using the ``"
order by neighborhood
title: Search neighborhoods
-.. _canned_queries_default_fragment:
+.. _canned_queries_options:
-Setting a default fragment
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+Additional canned query options
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Additional options can be specified for canned queries in the YAML or JSON configuration.
+
+hide_sql
+++++++++
+
+Canned queries default to displaying their SQL query at the top of the page. If the query is extremely long you may want to hide it by default, with a "show" link that can be used to make it visible.
+
+Add the ``"hide_sql": true`` option to hide the SQL query by default.
+
+fragment
+++++++++
Some plugins, such as `datasette-vega `__, can be configured by including additional data in the fragment hash of the URL - the bit that comes after a ``#`` symbol.
-You can set a default fragment hash that will be included in the link to the canned query from the database index page using the ``"fragment"`` key:
+You can set a default fragment hash that will be included in the link to the canned query from the database index page using the ``"fragment"`` key.
+
+This example demonstrates both ``fragment`` and ``hide_sql``:
.. code-block:: json
@@ -204,7 +218,8 @@ You can set a default fragment hash that will be included in the link to the can
"queries": {
"neighborhood_search": {
"sql": "select neighborhood, facet_cities.name, state\nfrom facetable join facet_cities on facetable.city_id = facet_cities.id\nwhere neighborhood like '%' || :text || '%' order by neighborhood;",
- "fragment": "fragment-goes-here"
+ "fragment": "fragment-goes-here",
+ "hide_sql": true
}
}
}
diff --git a/tests/fixtures.py b/tests/fixtures.py
index 93b7dce2f5..873f9d5510 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -360,6 +360,7 @@ def generate_sortable_rows(num):
"title": "Search neighborhoods",
"description_html": "Demonstrating simple like search",
"fragment": "fragment-goes-here",
+ "hide_sql": True,
},
},
}
diff --git a/tests/test_html.py b/tests/test_html.py
index 9f5b99e307..b1b6c1f315 100644
--- a/tests/test_html.py
+++ b/tests/test_html.py
@@ -1241,7 +1241,7 @@ def test_show_hide_sql_query(app_client):
def test_canned_query_with_hide_has_no_hidden_sql(app_client):
# For a canned query the show/hide should NOT have a hidden SQL field
# https://github.com/simonw/datasette/issues/1411
- response = app_client.get("/fixtures/neighborhood_search?_hide_sql=1")
+ response = app_client.get("/fixtures/pragma_cache_size?_hide_sql=1")
soup = Soup(response.body, "html.parser")
hiddens = soup.find("form").select("input[type=hidden]")
assert [
@@ -1249,6 +1249,55 @@ def test_canned_query_with_hide_has_no_hidden_sql(app_client):
] == [(hidden["name"], hidden["value"]) for hidden in hiddens]
+@pytest.mark.parametrize(
+ "hide_sql,querystring,expected_hidden,expected_show_hide_link,expected_show_hide_text",
+ (
+ (False, "", None, "/_memory/one?_hide_sql=1", "hide"),
+ (False, "?_hide_sql=1", "_hide_sql", "/_memory/one", "show"),
+ (True, "", None, "/_memory/one?_show_sql=1", "show"),
+ (True, "?_show_sql=1", "_show_sql", "/_memory/one", "hide"),
+ ),
+)
+def test_canned_query_show_hide_metadata_option(
+ hide_sql,
+ querystring,
+ expected_hidden,
+ expected_show_hide_link,
+ expected_show_hide_text,
+):
+ with make_app_client(
+ metadata={
+ "databases": {
+ "_memory": {
+ "queries": {
+ "one": {
+ "sql": "select 1 + 1",
+ "hide_sql": hide_sql,
+ }
+ }
+ }
+ }
+ },
+ memory=True,
+ ) as client:
+ expected_show_hide_fragment = '({})'.format(
+ expected_show_hide_link, expected_show_hide_text
+ )
+ response = client.get("/_memory/one" + querystring)
+ html = response.text
+ show_hide_fragment = html.split('')[1].split(
+ ""
+ )[0]
+ assert show_hide_fragment == expected_show_hide_fragment
+ if expected_hidden:
+ assert (
+ ''.format(expected_hidden)
+ in html
+ )
+ else:
+ assert '