kopia lustrzana https://github.com/simonw/datasette
rodzic
b72b2423c7
commit
8d5779acf0
|
@ -10,6 +10,9 @@
|
||||||
{% for url in extra_js_urls %}
|
{% for url in extra_js_urls %}
|
||||||
<script {% if url.module %}type="module" {% endif %}src="{{ url.url }}"{% if url.sri %} integrity="{{ url.sri }}" crossorigin="anonymous"{% endif %}></script>
|
<script {% if url.module %}type="module" {% endif %}src="{{ url.url }}"{% if url.sri %} integrity="{{ url.sri }}" crossorigin="anonymous"{% endif %}></script>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{%- if alternate_url_json -%}
|
||||||
|
<link rel="alternate" type="application/json+datasette" href="{{ alternate_url_json }}">
|
||||||
|
{%- endif -%}
|
||||||
{%- block extra_head %}{% endblock -%}
|
{%- block extra_head %}{% endblock -%}
|
||||||
</head>
|
</head>
|
||||||
<body class="{% block body_class %}{% endblock %}">
|
<body class="{% block body_class %}{% endblock %}">
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
{{- super() -}}
|
{{- super() -}}
|
||||||
<link rel="alternate" type="application/json+datasette" href="{{ alternate_url_json }}">
|
|
||||||
{% include "_codemirror.html" %}
|
{% include "_codemirror.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
{{- super() -}}
|
{{- super() -}}
|
||||||
<link rel="alternate" type="application/json+datasette" href="{{ alternate_url_json }}">
|
|
||||||
{% if columns %}
|
{% if columns %}
|
||||||
<style>
|
<style>
|
||||||
@media only screen and (max-width: 576px) {
|
@media only screen and (max-width: 576px) {
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
{{- super() -}}
|
{{- super() -}}
|
||||||
<link rel="alternate" type="application/json+datasette" href="{{ alternate_url_json }}">
|
|
||||||
<style>
|
<style>
|
||||||
@media only screen and (max-width: 576px) {
|
@media only screen and (max-width: 576px) {
|
||||||
{% for column in columns %}
|
{% for column in columns %}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
{% block extra_head %}
|
{% block extra_head %}
|
||||||
{{- super() -}}
|
{{- super() -}}
|
||||||
<link rel="alternate" type="application/json+datasette" href="{{ alternate_url_json }}">
|
|
||||||
<script src="{{ urls.static('table.js') }}" defer></script>
|
<script src="{{ urls.static('table.js') }}" defer></script>
|
||||||
<style>
|
<style>
|
||||||
@media only screen and (max-width: 576px) {
|
@media only screen and (max-width: 576px) {
|
||||||
|
|
|
@ -55,6 +55,7 @@ class DatasetteError(Exception):
|
||||||
|
|
||||||
class BaseView:
|
class BaseView:
|
||||||
ds = None
|
ds = None
|
||||||
|
has_json_alternate = True
|
||||||
|
|
||||||
def __init__(self, datasette):
|
def __init__(self, datasette):
|
||||||
self.ds = datasette
|
self.ds = datasette
|
||||||
|
@ -137,10 +138,20 @@ class BaseView:
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
# Hacky cheat to add extra headers
|
|
||||||
headers = {}
|
headers = {}
|
||||||
if "_extra_headers" in context:
|
if self.has_json_alternate:
|
||||||
headers.update(context["_extra_headers"])
|
alternate_url_json = self.ds.absolute_url(
|
||||||
|
request,
|
||||||
|
self.ds.urls.path(path_with_format(request=request, format="json")),
|
||||||
|
)
|
||||||
|
template_context["alternate_url_json"] = alternate_url_json
|
||||||
|
headers.update(
|
||||||
|
{
|
||||||
|
"Link": '{}; rel="alternate"; type="application/json+datasette"'.format(
|
||||||
|
alternate_url_json
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
return Response.html(
|
return Response.html(
|
||||||
await self.ds.render_template(
|
await self.ds.render_template(
|
||||||
template,
|
template,
|
||||||
|
|
|
@ -123,10 +123,6 @@ class DatabaseView(DataView):
|
||||||
|
|
||||||
attached_databases = [d.name for d in await db.attached_databases()]
|
attached_databases = [d.name for d in await db.attached_databases()]
|
||||||
|
|
||||||
alternate_url_json = self.ds.absolute_url(
|
|
||||||
request,
|
|
||||||
self.ds.urls.path(path_with_format(request=request, format="json")),
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
{
|
{
|
||||||
"database": database,
|
"database": database,
|
||||||
|
@ -144,7 +140,6 @@ class DatabaseView(DataView):
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alternate_url_json": alternate_url_json,
|
|
||||||
"database_actions": database_actions,
|
"database_actions": database_actions,
|
||||||
"show_hidden": request.args.get("_show_hidden"),
|
"show_hidden": request.args.get("_show_hidden"),
|
||||||
"editable": True,
|
"editable": True,
|
||||||
|
@ -153,11 +148,6 @@ class DatabaseView(DataView):
|
||||||
and not db.is_mutable
|
and not db.is_mutable
|
||||||
and not db.is_memory,
|
and not db.is_memory,
|
||||||
"attached_databases": attached_databases,
|
"attached_databases": attached_databases,
|
||||||
"_extra_headers": {
|
|
||||||
"Link": '{}; rel="alternate"; type="application/json+datasette"'.format(
|
|
||||||
alternate_url_json
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
(f"database-{to_css_class(database)}.html", "database.html"),
|
(f"database-{to_css_class(database)}.html", "database.html"),
|
||||||
)
|
)
|
||||||
|
@ -318,14 +308,7 @@ class QueryView(DataView):
|
||||||
else:
|
else:
|
||||||
|
|
||||||
async def extra_template():
|
async def extra_template():
|
||||||
alternate_url_json = self.ds.absolute_url(
|
|
||||||
request,
|
|
||||||
self.ds.urls.path(
|
|
||||||
path_with_format(request=request, format="json")
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
"alternate_url_json": alternate_url_json,
|
|
||||||
"request": request,
|
"request": request,
|
||||||
"path_with_added_args": path_with_added_args,
|
"path_with_added_args": path_with_added_args,
|
||||||
"path_with_removed_args": path_with_removed_args,
|
"path_with_removed_args": path_with_removed_args,
|
||||||
|
@ -333,11 +316,6 @@ class QueryView(DataView):
|
||||||
"canned_query": canned_query,
|
"canned_query": canned_query,
|
||||||
"success_message": request.args.get("_success") or "",
|
"success_message": request.args.get("_success") or "",
|
||||||
"canned_write": True,
|
"canned_write": True,
|
||||||
"_extra_headers": {
|
|
||||||
"Link": '{}; rel="alternate"; type="application/json+datasette"'.format(
|
|
||||||
alternate_url_json
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -470,12 +448,7 @@ class QueryView(DataView):
|
||||||
show_hide_link = path_with_added_args(request, {"_hide_sql": 1})
|
show_hide_link = path_with_added_args(request, {"_hide_sql": 1})
|
||||||
show_hide_text = "hide"
|
show_hide_text = "hide"
|
||||||
hide_sql = show_hide_text == "show"
|
hide_sql = show_hide_text == "show"
|
||||||
alternate_url_json = self.ds.absolute_url(
|
|
||||||
request,
|
|
||||||
self.ds.urls.path(path_with_format(request=request, format="json")),
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
"alternate_url_json": alternate_url_json,
|
|
||||||
"display_rows": display_rows,
|
"display_rows": display_rows,
|
||||||
"custom_sql": True,
|
"custom_sql": True,
|
||||||
"named_parameter_values": named_parameter_values,
|
"named_parameter_values": named_parameter_values,
|
||||||
|
@ -489,11 +462,6 @@ class QueryView(DataView):
|
||||||
"show_hide_text": show_hide_text,
|
"show_hide_text": show_hide_text,
|
||||||
"show_hide_hidden": markupsafe.Markup(show_hide_hidden),
|
"show_hide_hidden": markupsafe.Markup(show_hide_hidden),
|
||||||
"hide_sql": hide_sql,
|
"hide_sql": hide_sql,
|
||||||
"_extra_headers": {
|
|
||||||
"Link": '{}; rel="alternate"; type="application/json+datasette"'.format(
|
|
||||||
alternate_url_json
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -851,12 +851,7 @@ class TableView(RowTableShared):
|
||||||
for table_column in table_columns
|
for table_column in table_columns
|
||||||
if table_column not in columns
|
if table_column not in columns
|
||||||
]
|
]
|
||||||
alternate_url_json = self.ds.absolute_url(
|
|
||||||
request,
|
|
||||||
self.ds.urls.path(path_with_format(request=request, format="json")),
|
|
||||||
)
|
|
||||||
d = {
|
d = {
|
||||||
"alternate_url_json": alternate_url_json,
|
|
||||||
"table_actions": table_actions,
|
"table_actions": table_actions,
|
||||||
"use_rowid": use_rowid,
|
"use_rowid": use_rowid,
|
||||||
"filters": filters,
|
"filters": filters,
|
||||||
|
@ -887,11 +882,6 @@ class TableView(RowTableShared):
|
||||||
"metadata": metadata,
|
"metadata": metadata,
|
||||||
"view_definition": await db.get_view_definition(table),
|
"view_definition": await db.get_view_definition(table),
|
||||||
"table_definition": await db.get_table_definition(table),
|
"table_definition": await db.get_table_definition(table),
|
||||||
"_extra_headers": {
|
|
||||||
"Link": '{}; rel="alternate"; type="application/json+datasette"'.format(
|
|
||||||
alternate_url_json
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
d.update(extra_context_from_filters)
|
d.update(extra_context_from_filters)
|
||||||
return d
|
return d
|
||||||
|
@ -975,12 +965,7 @@ class RowView(RowTableShared):
|
||||||
)
|
)
|
||||||
for column in display_columns:
|
for column in display_columns:
|
||||||
column["sortable"] = False
|
column["sortable"] = False
|
||||||
alternate_url_json = self.ds.absolute_url(
|
|
||||||
request,
|
|
||||||
self.ds.urls.path(path_with_format(request=request, format="json")),
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
"alternate_url_json": alternate_url_json,
|
|
||||||
"foreign_key_tables": await self.foreign_key_tables(
|
"foreign_key_tables": await self.foreign_key_tables(
|
||||||
database, table, pk_values
|
database, table, pk_values
|
||||||
),
|
),
|
||||||
|
@ -995,11 +980,6 @@ class RowView(RowTableShared):
|
||||||
.get(database, {})
|
.get(database, {})
|
||||||
.get("tables", {})
|
.get("tables", {})
|
||||||
.get(table, {}),
|
.get(table, {}),
|
||||||
"_extra_headers": {
|
|
||||||
"Link": '{}; rel="alternate"; type="application/json+datasette"'.format(
|
|
||||||
alternate_url_json
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
|
@ -442,9 +442,9 @@ in ``metadata.json`` - see :ref:`label_columns`.
|
||||||
Discovering the JSON for a page
|
Discovering the JSON for a page
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
The :ref:`database <DatabaseView>`, :ref:`table <TableView>`, :ref:`custom/canned query <sql>` and :ref:`row <RowView>` HTML pages all provide a mechanism for discovering their JSON equivalents using the HTML ``link`` mechanism.
|
Most of the HTML pages served by Datasette provide a mechanism for discovering their JSON equivalents using the HTML ``link`` mechanism.
|
||||||
|
|
||||||
You can find this near the top of those pages, looking like this:
|
You can find this near the top of the source code of those pages, looking like this:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
@ -875,12 +875,14 @@ def test_trace_correctly_escaped(app_client):
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"path,expected",
|
"path,expected",
|
||||||
(
|
(
|
||||||
|
# Instance index page
|
||||||
|
("/", "http://localhost/.json"),
|
||||||
# Table page
|
# Table page
|
||||||
|
("/fixtures/facetable", "http://localhost/fixtures/facetable.json"),
|
||||||
(
|
(
|
||||||
"/fixtures/table%2Fwith%2Fslashes.csv",
|
"/fixtures/table%2Fwith%2Fslashes.csv",
|
||||||
"http://localhost/fixtures/table%2Fwith%2Fslashes.csv?_format=json",
|
"http://localhost/fixtures/table%2Fwith%2Fslashes.csv?_format=json",
|
||||||
),
|
),
|
||||||
("/fixtures/facetable", "http://localhost/fixtures/facetable.json"),
|
|
||||||
# Row page
|
# Row page
|
||||||
(
|
(
|
||||||
"/fixtures/no_primary_key/1",
|
"/fixtures/no_primary_key/1",
|
||||||
|
@ -901,6 +903,11 @@ def test_trace_correctly_escaped(app_client):
|
||||||
"/fixtures/neighborhood_search?text=town",
|
"/fixtures/neighborhood_search?text=town",
|
||||||
"http://localhost/fixtures/neighborhood_search.json?text=town",
|
"http://localhost/fixtures/neighborhood_search.json?text=town",
|
||||||
),
|
),
|
||||||
|
# /-/ pages
|
||||||
|
(
|
||||||
|
"/-/plugins",
|
||||||
|
"http://localhost/-/plugins.json",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_alternate_url_json(app_client, path, expected):
|
def test_alternate_url_json(app_client, path, expected):
|
||||||
|
|
Ładowanie…
Reference in New Issue