mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Export option: _shape=array&_nl=on for newline-delimited JSON
This commit is contained in:
parent
909cc8fbdf
commit
b5dd83981a
6 changed files with 58 additions and 11 deletions
|
|
@ -140,7 +140,13 @@
|
|||
{% if display_rows %}
|
||||
<div id="export" class="advanced-export">
|
||||
<h3>Advanced export</h3>
|
||||
<p>JSON shape: <a href="{{ url_json }}">default</a>, <a href="{{ append_querystring(url_json, '_shape=array') }}">array</a>{% if primary_keys %}, <a href="{{ append_querystring(url_json, '_shape=object') }}">object</a>{% endif %}</p>
|
||||
<p>JSON shape:
|
||||
<a href="{{ url_json }}">default</a>,
|
||||
<a href="{{ append_querystring(url_json, '_shape=array') }}">array</a>,
|
||||
<a href="{{ append_querystring(url_json, '_shape=array&_nl=on') }}">newline-delimited</a>{% if primary_keys %},
|
||||
<a href="{{ append_querystring(url_json, '_shape=object') }}">object</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<form action="{{ url_csv_path }}" method="get">
|
||||
<p>
|
||||
CSV options:
|
||||
|
|
|
|||
|
|
@ -432,10 +432,18 @@ class BaseView(RenderMixin):
|
|||
headers = {}
|
||||
if self.ds.cors:
|
||||
headers["Access-Control-Allow-Origin"] = "*"
|
||||
# Handle _nl option for _shape=array
|
||||
nl = request.args.get("_nl", "")
|
||||
if nl and shape == "array":
|
||||
body = "\n".join(json.dumps(item) for item in data)
|
||||
content_type = "text/plain"
|
||||
else:
|
||||
body = json.dumps(data, cls=CustomJSONEncoder)
|
||||
content_type = "application/json"
|
||||
r = response.HTTPResponse(
|
||||
json.dumps(data, cls=CustomJSONEncoder),
|
||||
body,
|
||||
status=status_code,
|
||||
content_type="application/json",
|
||||
content_type=content_type,
|
||||
headers=headers,
|
||||
)
|
||||
else:
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 24 KiB |
|
|
@ -61,11 +61,12 @@ options:
|
|||
|
||||
* ``?_shape=arrays`` - ``"rows"`` is the default option, shown above
|
||||
* ``?_shape=objects`` - ``"rows"`` is a list of JSON key/value objects
|
||||
* ``?_shape=array`` - the entire response is an array of objects
|
||||
* ``?_shape=arrayfirst`` - the entire response is a flat JSON array containing just the first value from each row
|
||||
* ``?_shape=object`` - the entire response is a JSON object keyed using the primary keys of the rows
|
||||
* ``?_shape=array`` - an JSON array of objects
|
||||
* ``?_shape=array&_nl=on`` - a newline-separated list of JSON objects
|
||||
* ``?_shape=arrayfirst`` - a flat JSON array containing just the first value from each row
|
||||
* ``?_shape=object`` - a JSON object keyed using the primary keys of the rows
|
||||
|
||||
``objects`` looks like this::
|
||||
``_shape=objects`` looks like this::
|
||||
|
||||
{
|
||||
"database": "sf-trees",
|
||||
|
|
@ -86,7 +87,7 @@ options:
|
|||
]
|
||||
}
|
||||
|
||||
``array`` looks like this::
|
||||
``_shape=array`` looks like this::
|
||||
|
||||
[
|
||||
{
|
||||
|
|
@ -103,11 +104,17 @@ options:
|
|||
}
|
||||
]
|
||||
|
||||
``arrayfirst`` looks like this::
|
||||
``_shape=array&_nl=on`` looks like this::
|
||||
|
||||
{"id": 1, "value": "Myoporum laetum :: Myoporum"}
|
||||
{"id": 2, "value": "Metrosideros excelsa :: New Zealand Xmas Tree"}
|
||||
{"id": 3, "value": "Pinus radiata :: Monterey Pine"}
|
||||
|
||||
``_shape=arrayfirst`` looks like this::
|
||||
|
||||
[1, 2, 3]
|
||||
|
||||
``object`` looks like this::
|
||||
``_shape=object`` looks like this::
|
||||
|
||||
{
|
||||
"1": {
|
||||
|
|
@ -140,6 +147,9 @@ querystring arguments:
|
|||
``?_shape=SHAPE``
|
||||
The shape of the JSON to return, documented above.
|
||||
|
||||
``?_nl=on``
|
||||
When used with ``?_shape=array`` produces newline-delimited JSON objects.
|
||||
|
||||
``?_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
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from .fixtures import ( # noqa
|
|||
make_app_client,
|
||||
METADATA,
|
||||
)
|
||||
import json
|
||||
import pytest
|
||||
import urllib
|
||||
|
||||
|
|
@ -547,6 +548,27 @@ def test_table_shape_array(app_client):
|
|||
}] == response.json
|
||||
|
||||
|
||||
def test_table_shape_array_nl(app_client):
|
||||
response = app_client.get(
|
||||
'/fixtures/simple_primary_key.json?_shape=array&_nl=on'
|
||||
)
|
||||
lines = response.text.split("\n")
|
||||
results = [json.loads(line) for line in lines]
|
||||
assert [{
|
||||
'id': '1',
|
||||
'content': 'hello',
|
||||
}, {
|
||||
'id': '2',
|
||||
'content': 'world',
|
||||
}, {
|
||||
'id': '3',
|
||||
'content': '',
|
||||
}, {
|
||||
'id': '4',
|
||||
'content': 'RENDER_CELL_DEMO',
|
||||
}] == results
|
||||
|
||||
|
||||
def test_table_shape_invalid(app_client):
|
||||
response = app_client.get(
|
||||
'/fixtures/simple_primary_key.json?_shape=invalid'
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ def test_table_csv_json_export_interface(app_client):
|
|||
assert [
|
||||
"simple_primary_key.json?id__gt=2",
|
||||
"simple_primary_key.json?id__gt=2&_shape=array",
|
||||
"simple_primary_key.json?id__gt=2&_shape=array&_nl=on",
|
||||
"simple_primary_key.json?id__gt=2&_shape=object"
|
||||
] == json_links
|
||||
# And the CSV form
|
||||
|
|
@ -796,7 +797,7 @@ def test_advanced_export_box(app_client, path, has_object, has_stream, has_expan
|
|||
assert response.status == 200
|
||||
soup = Soup(response.body, "html.parser")
|
||||
# JSON shape options
|
||||
expected_json_shapes = ["default", "array"]
|
||||
expected_json_shapes = ["default", "array", "newline-delimited"]
|
||||
if has_object:
|
||||
expected_json_shapes.append("object")
|
||||
div = soup.find("div", {"class": "advanced-export"})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue