kopia lustrzana https://github.com/simonw/datasette
rodzic
a30c5b220c
commit
4b7596ee5c
156
datasette/app.py
156
datasette/app.py
|
@ -383,7 +383,83 @@ class DatabaseDownload(BaseView):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TableView(BaseView):
|
class RowTableShared(BaseView):
|
||||||
|
async def make_display_rows(self, database, database_hash, table, rows, display_columns, pks, is_view, use_rowid):
|
||||||
|
# Get fancy with foreign keys
|
||||||
|
expanded = {}
|
||||||
|
tables = self.ds.inspect()[database]['tables']
|
||||||
|
table_info = tables.get(table) or {}
|
||||||
|
if table_info and not is_view:
|
||||||
|
foreign_keys = table_info['foreign_keys']['outgoing']
|
||||||
|
for fk in foreign_keys:
|
||||||
|
label_column = tables.get(fk['other_table'], {}).get('label_column')
|
||||||
|
if not label_column:
|
||||||
|
# We only link cells to other tables with label columns defined
|
||||||
|
continue
|
||||||
|
ids_to_lookup = set([row[fk['column']] for row in rows])
|
||||||
|
sql = 'select "{other_column}", "{label_column}" from {other_table} where "{other_column}" in ({placeholders})'.format(
|
||||||
|
other_column=fk['other_column'],
|
||||||
|
label_column=label_column,
|
||||||
|
other_table=escape_sqlite_table_name(fk['other_table']),
|
||||||
|
placeholders=', '.join(['?'] * len(ids_to_lookup)),
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
results = await self.execute(database, sql, list(set(ids_to_lookup)))
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
# Probably hit the timelimit
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
for id, value in results:
|
||||||
|
expanded[(fk['column'], id)] = (fk['other_table'], value)
|
||||||
|
|
||||||
|
to_return = []
|
||||||
|
for row in rows:
|
||||||
|
cells = []
|
||||||
|
# Unless we are a view, the first column is a link - either to the rowid
|
||||||
|
# or to the simple or compound primary key
|
||||||
|
if not is_view:
|
||||||
|
display_value = jinja2.Markup(
|
||||||
|
'<a href="/{database}-{database_hash}/{table}/{flat_pks}">{flat_pks}</a>'.format(
|
||||||
|
database=database,
|
||||||
|
database_hash=database_hash,
|
||||||
|
table=urllib.parse.quote_plus(table),
|
||||||
|
flat_pks=path_from_row_pks(row, pks, use_rowid),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cells.append({
|
||||||
|
'column': 'rowid' if use_rowid else 'Link',
|
||||||
|
'value': display_value,
|
||||||
|
})
|
||||||
|
|
||||||
|
for value, column in zip(row, display_columns):
|
||||||
|
if use_rowid and column == 'rowid':
|
||||||
|
# We already showed this in the linked first column
|
||||||
|
continue
|
||||||
|
elif (column, value) in expanded:
|
||||||
|
other_table, label = expanded[(column, value)]
|
||||||
|
display_value = jinja2.Markup(
|
||||||
|
# TODO: Escape id/label/etc so no XSS here
|
||||||
|
'<a href="/{database}-{database_hash}/{table}/{id}">{label}</a> <em>{id}</em>'.format(
|
||||||
|
database=database,
|
||||||
|
database_hash=database_hash,
|
||||||
|
table=escape_sqlite_table_name(other_table),
|
||||||
|
id=value,
|
||||||
|
label=label,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif value is None:
|
||||||
|
display_value = jinja2.Markup(' ')
|
||||||
|
else:
|
||||||
|
display_value = str(value)
|
||||||
|
cells.append({
|
||||||
|
'column': column,
|
||||||
|
'value': display_value,
|
||||||
|
})
|
||||||
|
to_return.append(cells)
|
||||||
|
return to_return
|
||||||
|
|
||||||
|
|
||||||
|
class TableView(RowTableShared):
|
||||||
template = 'table.html'
|
template = 'table.html'
|
||||||
|
|
||||||
async def data(self, request, name, hash, table):
|
async def data(self, request, name, hash, table):
|
||||||
|
@ -575,82 +651,8 @@ class TableView(BaseView):
|
||||||
'next_url': next_url,
|
'next_url': next_url,
|
||||||
}, extra_template
|
}, extra_template
|
||||||
|
|
||||||
async def make_display_rows(self, database, database_hash, table, rows, display_columns, pks, is_view, use_rowid):
|
|
||||||
# Get fancy with foreign keys
|
|
||||||
expanded = {}
|
|
||||||
tables = self.ds.inspect()[database]['tables']
|
|
||||||
table_info = tables.get(table) or {}
|
|
||||||
if table_info and not is_view:
|
|
||||||
foreign_keys = table_info['foreign_keys']['outgoing']
|
|
||||||
for fk in foreign_keys:
|
|
||||||
label_column = tables.get(fk['other_table'], {}).get('label_column')
|
|
||||||
if not label_column:
|
|
||||||
# We only link cells to other tables with label columns defined
|
|
||||||
continue
|
|
||||||
ids_to_lookup = set([row[fk['column']] for row in rows])
|
|
||||||
sql = 'select "{other_column}", "{label_column}" from {other_table} where "{other_column}" in ({placeholders})'.format(
|
|
||||||
other_column=fk['other_column'],
|
|
||||||
label_column=label_column,
|
|
||||||
other_table=escape_sqlite_table_name(fk['other_table']),
|
|
||||||
placeholders=', '.join(['?'] * len(ids_to_lookup)),
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
results = await self.execute(database, sql, list(set(ids_to_lookup)))
|
|
||||||
except sqlite3.OperationalError:
|
|
||||||
# Probably hit the timelimit
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
for id, value in results:
|
|
||||||
expanded[(fk['column'], id)] = (fk['other_table'], value)
|
|
||||||
|
|
||||||
to_return = []
|
class RowView(RowTableShared):
|
||||||
for row in rows:
|
|
||||||
cells = []
|
|
||||||
# Unless we are a view, the first column is a link - either to the rowid
|
|
||||||
# or to the simple or compound primary key
|
|
||||||
if not is_view:
|
|
||||||
display_value = jinja2.Markup(
|
|
||||||
'<a href="/{database}-{database_hash}/{table}/{flat_pks}">{flat_pks}</a>'.format(
|
|
||||||
database=database,
|
|
||||||
database_hash=database_hash,
|
|
||||||
table=urllib.parse.quote_plus(table),
|
|
||||||
flat_pks=path_from_row_pks(row, pks, use_rowid),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
cells.append({
|
|
||||||
'column': 'rowid' if use_rowid else 'Link',
|
|
||||||
'value': display_value,
|
|
||||||
})
|
|
||||||
|
|
||||||
for value, column in zip(row, display_columns):
|
|
||||||
if use_rowid and column == 'rowid':
|
|
||||||
# We already showed this in the linked first column
|
|
||||||
continue
|
|
||||||
elif (column, value) in expanded:
|
|
||||||
other_table, label = expanded[(column, value)]
|
|
||||||
display_value = jinja2.Markup(
|
|
||||||
# TODO: Escape id/label/etc so no XSS here
|
|
||||||
'<a href="/{database}-{database_hash}/{table}/{id}">{label}</a> <em>{id}</em>'.format(
|
|
||||||
database=database,
|
|
||||||
database_hash=database_hash,
|
|
||||||
table=escape_sqlite_table_name(other_table),
|
|
||||||
id=value,
|
|
||||||
label=label,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif value is None:
|
|
||||||
display_value = jinja2.Markup(' ')
|
|
||||||
else:
|
|
||||||
display_value = str(value)
|
|
||||||
cells.append({
|
|
||||||
'column': column,
|
|
||||||
'value': display_value,
|
|
||||||
})
|
|
||||||
to_return.append(cells)
|
|
||||||
return to_return
|
|
||||||
|
|
||||||
|
|
||||||
class RowView(BaseView):
|
|
||||||
template = 'row.html'
|
template = 'row.html'
|
||||||
|
|
||||||
async def data(self, request, name, hash, table, pk_path):
|
async def data(self, request, name, hash, table, pk_path):
|
||||||
|
@ -683,6 +685,8 @@ class RowView(BaseView):
|
||||||
return {
|
return {
|
||||||
'database_hash': hash,
|
'database_hash': hash,
|
||||||
'foreign_key_tables': await self.foreign_key_tables(name, table, pk_values),
|
'foreign_key_tables': await self.foreign_key_tables(name, table, pk_values),
|
||||||
|
'display_columns': columns,
|
||||||
|
'display_rows': await self.make_display_rows(name, hash, table, rows, columns, pks, False, use_rowid),
|
||||||
}
|
}
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<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>{{ cell.value }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
|
@ -19,22 +19,7 @@
|
||||||
|
|
||||||
<p>This data as <a href="{{ url_json }}">.json</a>, <a href="{{ url_jsono }}">.jsono</a></p>
|
<p>This data as <a href="{{ url_json }}">.json</a>, <a href="{{ url_jsono }}">.jsono</a></p>
|
||||||
|
|
||||||
<table>
|
{% include "_rows_and_columns.html" %}
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{% for column in columns %}<th scope="col">{{ column }}</th>{% endfor %}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for row in rows %}
|
|
||||||
<tr>
|
|
||||||
{% for td in row %}
|
|
||||||
<td>{{ td }}</td>
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% if foreign_key_tables %}
|
{% if foreign_key_tables %}
|
||||||
<h2>Links from other tables</h2>
|
<h2>Links from other tables</h2>
|
||||||
|
|
|
@ -78,22 +78,7 @@
|
||||||
|
|
||||||
<p>Returned {% if truncated %}more than {% endif %}{{ "{:,}".format(display_rows|length) }} row{% if display_rows|length == 1 %}{% else %}s{% endif %}</p>
|
<p>Returned {% if truncated %}more than {% endif %}{{ "{:,}".format(display_rows|length) }} row{% if display_rows|length == 1 %}{% else %}s{% endif %}</p>
|
||||||
|
|
||||||
<table>
|
{% include "_rows_and_columns.html" %}
|
||||||
<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>{{ cell.value }}</td>
|
|
||||||
{% endfor %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% if next_url %}
|
{% if next_url %}
|
||||||
<p><a href="{{ next_url }}">Next page</a></p>
|
<p><a href="{{ next_url }}">Next page</a></p>
|
||||||
|
|
Ładowanie…
Reference in New Issue