Compare commits

...

5 commits

Author SHA1 Message Date
Simon Willison
1add905532 Updated custom template docs, refs #521 2019-07-02 20:13:34 -07:00
Simon Willison
94d856c9a1 Unit test for _table custom template, refs #521 2019-07-02 20:06:22 -07:00
Simon Willison
7d6b0d6762 Rename _rows_and_columns.html to _table.html, refs #521 2019-07-02 17:51:54 -07:00
Simon Willison
43a5567be8 Default to raw value, use Row.display(key) for display, refs #521 2019-06-25 05:21:10 -07:00
Simon Willison
2b847240bb New experimental Row() for templates, refs #521 2019-06-25 05:02:42 -07:00
9 changed files with 119 additions and 82 deletions

View file

@ -24,7 +24,7 @@
<p>This data as {% for name, url in renderers.items() %}<a href="{{ url }}">{{ name }}</a>{{ ", " if not loop.last }}{% endfor %}</p>
{% include custom_rows_and_columns_templates %}
{% include custom_table_templates %}
{% if foreign_key_tables %}
<h2>Links from other tables</h2>

View file

@ -145,7 +145,7 @@
</div>
{% endif %}
{% include custom_rows_and_columns_templates %}
{% include custom_table_templates %}
{% if next_url %}
<p><a href="{{ next_url }}">Next page</a></p>

View file

@ -33,6 +33,35 @@ LINK_WITH_LABEL = (
LINK_WITH_VALUE = '<a href="/{database}/{table}/{link_id}">{id}</a>'
class Row:
def __init__(self, cells):
self.cells = cells
def __iter__(self):
return iter(self.cells)
def __getitem__(self, key):
for cell in self.cells:
if cell["column"] == key:
return cell["raw"]
raise KeyError
def display(self, key):
for cell in self.cells:
if cell["column"] == key:
return cell["value"]
return None
def __str__(self):
d = {
key: self[key]
for key in [
c["column"] for c in self.cells if not c.get("is_special_link_column")
]
}
return json.dumps(d, default=repr, indent=2)
class RowTableShared(DataView):
async def sortable_columns_for_table(self, database, table, use_rowid):
db = self.ds.databases[database]
@ -76,18 +105,18 @@ class RowTableShared(DataView):
# Unless we are a view, the first column is a link - either to the rowid
# or to the simple or compound primary key
if link_column:
is_special_link_column = len(pks) != 1
pk_path = path_from_row_pks(row, pks, not pks, False)
cells.append(
{
"column": pks[0] if len(pks) == 1 else "Link",
"is_special_link_column": is_special_link_column,
"raw": pk_path,
"value": jinja2.Markup(
'<a href="/{database}/{table}/{flat_pks_quoted}">{flat_pks}</a>'.format(
database=database,
table=urllib.parse.quote_plus(table),
flat_pks=str(
jinja2.escape(
path_from_row_pks(row, pks, not pks, False)
)
),
flat_pks=str(jinja2.escape(pk_path)),
flat_pks_quoted=path_from_row_pks(row, pks, not pks),
)
),
@ -159,8 +188,8 @@ class RowTableShared(DataView):
if truncate_cells and len(display_value) > truncate_cells:
display_value = display_value[:truncate_cells] + u"\u2026"
cells.append({"column": column, "value": display_value})
cell_rows.append(cells)
cells.append({"column": column, "value": display_value, "raw": value})
cell_rows.append(Row(cells))
if link_column:
# Add the link column header.
@ -715,14 +744,14 @@ class TableView(RowTableShared):
"sort": sort,
"sort_desc": sort_desc,
"disable_sort": is_view,
"custom_rows_and_columns_templates": [
"_rows_and_columns-{}-{}.html".format(
"custom_table_templates": [
"_table-{}-{}.html".format(
to_css_class(database), to_css_class(table)
),
"_rows_and_columns-table-{}-{}.html".format(
"_table-table-{}-{}.html".format(
to_css_class(database), to_css_class(table)
),
"_rows_and_columns.html",
"_table.html",
],
"metadata": metadata,
"view_definition": await db.get_view_definition(table),
@ -799,14 +828,14 @@ class RowView(RowTableShared):
),
"display_columns": display_columns,
"display_rows": display_rows,
"custom_rows_and_columns_templates": [
"_rows_and_columns-{}-{}.html".format(
"custom_table_templates": [
"_table-{}-{}.html".format(
to_css_class(database), to_css_class(table)
),
"_rows_and_columns-row-{}-{}.html".format(
"_table-row-{}-{}.html".format(
to_css_class(database), to_css_class(table)
),
"_rows_and_columns.html",
"_table.html",
],
"metadata": (self.ds.metadata("databases") or {})
.get(database, {})

View file

@ -144,15 +144,15 @@ The lookup rules Datasette uses are as follows::
row-mydatabase-mytable.html
row.html
Rows and columns include on table page:
_rows_and_columns-table-mydatabase-mytable.html
_rows_and_columns-mydatabase-mytable.html
_rows_and_columns.html
Table of rows and columns include on table page:
_table-table-mydatabase-mytable.html
_table-mydatabase-mytable.html
_table.html
Rows and columns include on row page:
_rows_and_columns-row-mydatabase-mytable.html
_rows_and_columns-mydatabase-mytable.html
_rows_and_columns.html
Table of rows and columns include on row page:
_table-row-mydatabase-mytable.html
_table-mydatabase-mytable.html
_table.html
If a table name has spaces or other unexpected characters in it, the template
filename will follow the same rules as our custom ``<body>`` CSS classes - for
@ -189,38 +189,28 @@ content you can do so by creating a ``row.html`` template like this::
Note the ``default:row.html`` template name, which ensures Jinja will inherit
from the default template.
The ``_rows_and_columns.html`` template is included on both the row and the table
page, and displays the content of the row. The default ``_rows_and_columns.html`` template
`can be seen here <https://github.com/simonw/datasette/blob/master/datasette/templates/_rows_and_columns.html>`_.
The ``_table.html`` template is included by both the row and the table pages,
and a list of rows. The default ``_table.html`` template renders them as an
HTML template and `can be seen here <https://github.com/simonw/datasette/blob/master/datasette/templates/_table.html>`_.
You can provide a custom template that applies to all of your databases and
tables, or you can provide custom templates for specific tables using the
template naming scheme described above.
Say for example you want to output a certain column as unescaped HTML. You could
provide a custom ``_rows_and_columns.html`` template like this::
If you want to present your data in a format other than an HTML table, you
can do so by looping through ``display_rows`` in your own ``_table.html``
template. You can use ``{{ row["column_name"] }}`` to output the raw value
of a specific column.
<table>
<thead>
<tr>
{% for column in display_columns %}
<th scope="col">{{ column }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in display_rows %}
<tr>
{% for cell in row %}
<td>
{% if cell.column == 'description' %}
{{ cell.value|safe }}
{% else %}
{{ cell.value }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
If you want to output the rendered HTML version of a column, including any
links to foreign keys, you can use ``{{ row.display("column_name") }}``.
Here is an example of a custom ``_table.html`` template::
{% for row in display_rows %}
<div>
<h2>{{ row["title"] }}</h2>
<p>{{ row["description"] }}<lp>
<p>Category: {{ row.display("category_id") }}</p>
</div>
{% endfor %}

View file

@ -101,6 +101,7 @@ def make_app_client(
extra_databases=None,
inspect_data=None,
static_mounts=None,
template_dir=None,
):
with tempfile.TemporaryDirectory() as tmpdir:
filepath = os.path.join(tmpdir, filename)
@ -143,6 +144,7 @@ def make_app_client(
config=config,
inspect_data=inspect_data,
static_mounts=static_mounts,
template_dir=template_dir,
)
ds.sqlite_functions.append(("sleep", 1, lambda n: time.sleep(float(n))))
client = TestClient(ds.app())

View file

@ -964,3 +964,16 @@ def test_metadata_json_html(app_client):
assert response.status == 200
pre = Soup(response.body, "html.parser").find("pre")
assert METADATA == json.loads(pre.text)
def test_custom_table_include():
for client in make_app_client(
template_dir=str(pathlib.Path(__file__).parent / "test_templates")
):
response = client.get("/fixtures/complex_foreign_keys")
assert response.status == 200
assert (
'<div class="custom-table-row">'
'1 - 2 - <a href="/fixtures/simple_primary_key/1">hello</a> <em>1</em>'
"</div>"
) == str(Soup(response.text, "html.parser").select_one("div.custom-table-row"))

View file

@ -0,0 +1,3 @@
{% for row in display_rows %}
<div class="custom-table-row">{{ row["f1"] }} - {{ row["f2"] }} - {{ row.display("f3") }}</div>
{% endfor %}