mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
parent
e11cb4c664
commit
bc6a9b4564
6 changed files with 92 additions and 2 deletions
|
|
@ -105,6 +105,11 @@ h2 em {
|
|||
font-style: normal;
|
||||
font-weight: lighter;
|
||||
}
|
||||
.extra-wheres ul, .extra-wheres li {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
form.sql textarea {
|
||||
border: 1px solid #ccc;
|
||||
width: 70%;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,17 @@
|
|||
</div>
|
||||
</form>
|
||||
|
||||
{% if extra_wheres_for_ui %}
|
||||
<div class="extra-wheres">
|
||||
<h3>{{ extra_wheres_for_ui|length }} extra where clause{% if extra_wheres_for_ui|length != 1 %}s{% endif %}</h3>
|
||||
<ul>
|
||||
{% for extra_where in extra_wheres_for_ui %}
|
||||
<li><code>{{ extra_where.text }}</code> [<a href="{{ extra_where.remove_url }}">remove</a>]</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if query.sql and config.allow_sql %}
|
||||
<p><a class="not-underlined" title="{{ query.sql }}" href="{{ database_url(database) }}?{{ {'sql': query.sql}|urlencode|safe }}{% if query.params %}&{{ query.params|urlencode|safe }}{% endif %}">✎ <span class="underlined">View and edit SQL</span></a></p>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -295,6 +295,20 @@ class TableView(RowTableShared):
|
|||
filters = Filters(sorted(other_args.items()), units, ureg)
|
||||
where_clauses, params = filters.build_where_clauses(table)
|
||||
|
||||
extra_wheres_for_ui = []
|
||||
# Add _where= from querystring
|
||||
if "_where" in request.args:
|
||||
if not self.ds.config("allow_sql"):
|
||||
raise DatasetteError("_where= is not allowed", status=400)
|
||||
else:
|
||||
where_clauses.extend(request.args["_where"])
|
||||
extra_wheres_for_ui = [{
|
||||
"text": text,
|
||||
"remove_url": path_with_removed_args(
|
||||
request, {"_where": text}
|
||||
)
|
||||
} for text in request.args["_where"]]
|
||||
|
||||
# _search support:
|
||||
fts_table = special_args.get("_fts_table")
|
||||
fts_table = fts_table or table_metadata.get("fts_table")
|
||||
|
|
@ -751,6 +765,7 @@ class TableView(RowTableShared):
|
|||
key=lambda f: (len(f["results"]), f["name"]),
|
||||
reverse=True
|
||||
),
|
||||
"extra_wheres_for_ui": extra_wheres_for_ui,
|
||||
"form_hidden_args": form_hidden_args,
|
||||
"facet_hideable": lambda facet: facet not in metadata_facets,
|
||||
"is_sortable": any(c["sortable"] for c in display_columns),
|
||||
|
|
|
|||
|
|
@ -206,6 +206,21 @@ The Datasette table view takes a number of special querystring arguments:
|
|||
Like ``_search=`` but allows you to specify the column to be searched, as
|
||||
opposed to searching all columns that have been indexed by FTS.
|
||||
|
||||
``?_where=SQL-fragment``
|
||||
If the :ref:`config_allow_sql` config option is enabled, this parameter
|
||||
can be used to pass one or more additional SQL fragments to be used in the
|
||||
`WHERE` clause of the SQL used to query the table.
|
||||
|
||||
This is particularly useful if you are building a JavaScript application
|
||||
that needs to do something creative but still wants the other conveniences
|
||||
provided by the table view (such as faceting) and hence would like not to
|
||||
have to construct a completely custom SQL query.
|
||||
|
||||
Some examples:
|
||||
|
||||
* `facetable?_where=state="MI"&_where=city_id=3 <https://latest.datasette.io/facetable?_where=state=%22MI%22&_where=city_id=3>`__
|
||||
* `facetable?_where=city_id in (select id from facet_cities where name != "Detroit") <https://latest.datasette.io/fixtures/facetable?_where=city_id%20in%20(select%20id%20from%20facet_cities%20where%20name%20!=%20%22Detroit%22)>`__
|
||||
|
||||
``?_group_count=COLUMN``
|
||||
Executes a SQL query that returns a count of the number of rows matching
|
||||
each unique value in that column, with the most common ordered first.
|
||||
|
|
|
|||
|
|
@ -443,9 +443,11 @@ def test_allow_sql_off():
|
|||
for client in make_app_client(config={
|
||||
'allow_sql': False,
|
||||
}):
|
||||
assert 400 == client.get(
|
||||
response = client.get(
|
||||
"/fixtures.json?sql=select+sleep(0.01)"
|
||||
).status
|
||||
)
|
||||
assert 400 == response.status
|
||||
assert 'sql= is not allowed' == response.json['error']
|
||||
|
||||
|
||||
def test_table_json(app_client):
|
||||
|
|
@ -913,6 +915,34 @@ def test_table_filter_json_arraycontains(app_client):
|
|||
] == response.json['rows']
|
||||
|
||||
|
||||
def test_table_filter_extra_where(app_client):
|
||||
response = app_client.get(
|
||||
"/fixtures/facetable.json?_where=neighborhood='Dogpatch'"
|
||||
)
|
||||
assert [
|
||||
[2, 1, 1, 'CA', 1, 'Dogpatch', '["tag1", "tag3"]']
|
||||
] == response.json['rows']
|
||||
|
||||
|
||||
def test_table_filter_extra_where_invalid(app_client):
|
||||
response = app_client.get(
|
||||
"/fixtures/facetable.json?_where=neighborhood=Dogpatch'"
|
||||
)
|
||||
assert 400 == response.status
|
||||
assert 'Invalid SQL' == response.json['title']
|
||||
|
||||
|
||||
def test_table_filter_extra_where_disabled_if_no_sql_allowed():
|
||||
for client in make_app_client(config={
|
||||
'allow_sql': False,
|
||||
}):
|
||||
response = client.get(
|
||||
"/fixtures/facetable.json?_where=neighborhood='Dogpatch'"
|
||||
)
|
||||
assert 400 == response.status
|
||||
assert '_where= is not allowed' == response.json['error']
|
||||
|
||||
|
||||
def test_max_returned_rows(app_client):
|
||||
response = app_client.get(
|
||||
'/fixtures.json?sql=select+content+from+no_primary_key'
|
||||
|
|
|
|||
|
|
@ -869,3 +869,17 @@ def test_show_hide_sql_query(app_client):
|
|||
] == [
|
||||
(hidden['name'], hidden['value']) for hidden in hiddens
|
||||
]
|
||||
|
||||
|
||||
def test_extra_where_clauses(app_client):
|
||||
response = app_client.get(
|
||||
"/fixtures/facetable?_where=neighborhood='Dogpatch'&_where=city_id=1"
|
||||
)
|
||||
soup = Soup(response.body, "html.parser")
|
||||
div = soup.select(".extra-wheres")[0]
|
||||
assert "2 extra where clauses" == div.find("h3").text
|
||||
hrefs = [a["href"] for a in div.findAll("a")]
|
||||
assert [
|
||||
"/fixtures/facetable?_where=city_id%3D1",
|
||||
"/fixtures/facetable?_where=neighborhood%3D%27Dogpatch%27"
|
||||
] == hrefs
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue