mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Compare commits
5 commits
main
...
better-tem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1add905532 | ||
|
|
94d856c9a1 | ||
|
|
7d6b0d6762 | ||
|
|
43a5567be8 | ||
|
|
2b847240bb |
9 changed files with 119 additions and 82 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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, {})
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
3
tests/test_templates/_table.html
Normal file
3
tests/test_templates/_table.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{% for row in display_rows %}
|
||||
<div class="custom-table-row">{{ row["f1"] }} - {{ row["f2"] }} - {{ row.display("f3") }}</div>
|
||||
{% endfor %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue