New hide_sql canned query option, refs #1422

pull/1399/merge
Simon Willison 2021-08-06 22:09:00 -07:00
rodzic b7037f5ece
commit 66e143c76e
6 zmienionych plików z 111 dodań i 14 usunięć

Wyświetl plik

@ -33,7 +33,9 @@
{% block description_source_license %}{% include "_description_source_license.html" %}{% endblock %}
<form class="sql" action="{{ urls.database(database) }}{% if canned_query %}/{{ canned_query }}{% endif %}" method="{% if canned_write %}post{% else %}get{% endif %}">
<h3>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 %} <span class="show-hide-sql">{% if hide_sql %}(<a href="{{ path_with_removed_args(request, {'_hide_sql': '1'}) }}">show</a>){% else %}(<a href="{{ path_with_added_args(request, {'_hide_sql': '1'}) }}">hide</a>){% endif %}</span>{% endif %}</h3>
<h3>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 %}
<span class="show-hide-sql">(<a href="{{ show_hide_link }}">{{ show_hide_text }}</a>)</span>
{% endif %}</h3>
{% if error %}
<p class="message-error">{{ error }}</p>
{% endif %}
@ -44,8 +46,11 @@
<pre id="sql-query">{% if query %}{{ query.sql }}{% endif %}</pre>
{% endif %}
{% else %}
{% if not canned_query %}<input type="hidden" name="sql" value="{% if query and query.sql %}{{ query.sql }}{% else %}select * from {{ tables[0].name|escape_sqlite }}{% endif %}">{% endif %}
<input type="hidden" name="_hide_sql" value="1">
{% if not canned_query %}
<input type="hidden" name="sql"
value="{% if query and query.sql %}{{ query.sql }}{% else %}select * from {{ tables[0].name|escape_sqlite }}{% endif %}"
>
{% endif %}
{% endif %}
{% if named_parameter_values %}
<h3>Query parameters</h3>
@ -54,9 +59,10 @@
{% endfor %}
{% endif %}
<p>
<button id="sql-format" type="button" hidden>Format SQL</button>
{% if not hide_sql %}<button id="sql-format" type="button" hidden>Format SQL</button>{% endif %}
{% if canned_write %}<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">{% endif %}
<input type="submit" value="Run SQL">
{{ show_hide_hidden }}
{% if canned_query and edit_sql_url %}<a href="{{ edit_sql_url }}" class="canned-query-edit-sql">Edit SQL</a>{% endif %}
</p>
</form>

Wyświetl plik

@ -5,6 +5,8 @@ import json
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 @@ class QueryView(DataView):
}
)
)
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 = (
'<input type="hidden" name="_show_sql" value="1">'
)
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 = (
'<input type="hidden" name="_hide_sql" value="1">'
)
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 @@ class QueryView(DataView):
"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 (

Wyświetl plik

@ -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 <https://github.com/simonw/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 <https://github.com/simonw/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`)

Wyświetl plik

@ -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 <https://github.com/simonw/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
}
}
}

Wyświetl plik

@ -360,6 +360,7 @@ METADATA = {
"title": "Search neighborhoods",
"description_html": "<b>Demonstrating</b> simple like search",
"fragment": "fragment-goes-here",
"hide_sql": True,
},
},
}

Wyświetl plik

@ -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 = '(<a href="{}">{}</a>)'.format(
expected_show_hide_link, expected_show_hide_text
)
response = client.get("/_memory/one" + querystring)
html = response.text
show_hide_fragment = html.split('<span class="show-hide-sql">')[1].split(
"</span>"
)[0]
assert show_hide_fragment == expected_show_hide_fragment
if expected_hidden:
assert (
'<input type="hidden" name="{}" value="1">'.format(expected_hidden)
in html
)
else:
assert '<input type="hidden" ' not in html
def test_extra_where_clauses(app_client):
response = app_client.get(
"/fixtures/facetable?_where=neighborhood='Dogpatch'&_where=city_id=1"