New ?_json=colname argument for returning unescaped JSON

Also extracted docs for special JSON arguments into a new section.

Closes #31
custom-router
Simon Willison 2018-05-28 11:08:39 -07:00
rodzic 02870e5731
commit 76d11eb768
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 17E2DEA2588B7F52
3 zmienionych plików z 91 dodań i 10 usunięć

Wyświetl plik

@ -183,6 +183,15 @@ class BaseView(RenderMixin):
forward_querystring=False,
)
# Handle the _json= parameter which may modify data["rows"]
json_cols = []
if "_json" in request.args:
json_cols = request.args["_json"]
if json_cols and "rows" in data and "columns" in data:
data["rows"] = convert_specific_columns_to_json(
data["rows"], data["columns"], json_cols,
)
# Deal with the _shape option
shape = request.args.get("_shape", "arrays")
if shape == "arrayfirst":
@ -323,3 +332,22 @@ class BaseView(RenderMixin):
"canned_query": canned_query,
"config": self.ds.config,
}, templates
def convert_specific_columns_to_json(rows, columns, json_cols):
json_cols = set(json_cols)
if not json_cols.intersection(columns):
return rows
new_rows = []
for row in rows:
new_row = []
for value, column in zip(row, columns):
if column in json_cols:
try:
value = json.loads(value)
except (TypeError, ValueError) as e:
print(e)
pass
new_row.append(value)
new_rows.append(new_row)
return new_rows

Wyświetl plik

@ -131,6 +131,33 @@ this format.
The ``object`` keys are always strings. If your table has a compound primary
key, the ``object`` keys will be a comma-separated string.
Special JSON arguments
----------------------
Every Datasette endpoint that can return JSON also accepts the following
querystring arguments:
``?_shape=SHAPE``
The shape of the JSON to return, documented above.
``?_json=COLUMN1&_json=COLUMN2``
If any of your SQLite columns contain JSON values, you can use one or more
``_json=`` parameters to request that those columns be returned as regular
JSON. Without this argument those columns will be returned as JSON objects
that have been double-encoded into a JSON string value.
Compare `this query without the argument <https://fivethirtyeight.datasettes.com/fivethirtyeight.json?sql=select+%27{%22this+is%22%3A+%22a+json+object%22}%27+as+d&_shape=array>`_ to `this query using the argument <https://fivethirtyeight.datasettes.com/fivethirtyeight.json?sql=select+%27{%22this+is%22%3A+%22a+json+object%22}%27+as+d&_shape=array&_json=d>`_
``?_timelimit=MS``
Sets a custom time limit for the query in ms. You can use this for optimistic
queries where you would like Datasette to give up if the query takes too
long, for example if you want to implement autocomplete search but only if
it can be executed in less than 10ms.
``?_ttl=SECONDS``
For how many seconds should this response be cached by HTTP proxies? Use
``?_ttl=0`` to disable HTTP caching entirely for this request.
Special table arguments
-----------------------
@ -163,16 +190,6 @@ The Datasette table view takes a number of special querystring arguments:
You can pass multiple ``_group_count`` columns to return counts against
unique combinations of those columns.
``?_timelimit=MS``
Sets a custom time limit for the query in ms. You can use this for optimistic
queries where you would like Datasette to give up if the query takes too
long, for example if you want to implement autocomplete search but only if
it can be executed in less than 10ms.
``?_ttl=SECONDS``
For how many seconds should this response be cached by HTTP proxies? Use
``?_ttl=0`` to disable HTTP caching entirely for this request.
``?_next=TOKEN``
Pagination by continuation token - pass the token that was returned in the
``"next"`` property by the previous page.

Wyświetl plik

@ -1151,3 +1151,39 @@ def test_suggest_facets_off():
def test_ttl_parameter(app_client, path, expected_cache_control):
response = app_client.get(path, gather_request=False)
assert expected_cache_control == response.headers['Cache-Control']
test_json_columns_default_expected = [{
"intval": 1,
"strval": "s",
"floatval": 0.5,
"jsonval": "{\"foo\": \"bar\"}"
}]
@pytest.mark.parametrize("extra_args,expected", [
("", test_json_columns_default_expected),
("&_json=intval", test_json_columns_default_expected),
("&_json=strval", test_json_columns_default_expected),
("&_json=floatval", test_json_columns_default_expected),
("&_json=jsonval", [{
"intval": 1,
"strval": "s",
"floatval": 0.5,
"jsonval": {
"foo": "bar"
}
}])
])
def test_json_columns(app_client, extra_args, expected):
sql = '''
select 1 as intval, "s" as strval, 0.5 as floatval,
'{"foo": "bar"}' as jsonval
'''
path = "/test_tables.json?" + urllib.parse.urlencode({
"sql": sql,
"_shape": "array"
})
path += extra_args
response = app_client.get(path, gather_request=False)
assert expected == response.json