kopia lustrzana https://github.com/simonw/datasette
rodzic
fd3a33989a
commit
59580d02da
|
@ -243,6 +243,8 @@ class DatabaseView(BaseView):
|
||||||
template = 'database.html'
|
template = 'database.html'
|
||||||
|
|
||||||
async def data(self, request, name, hash):
|
async def data(self, request, name, hash):
|
||||||
|
if request.args.get('sql'):
|
||||||
|
return await self.custom_sql(request, name, hash)
|
||||||
tables = []
|
tables = []
|
||||||
table_metadata = self.ds.metadata()[name]['tables']
|
table_metadata = self.ds.metadata()[name]['tables']
|
||||||
for table_name, table_rows in table_metadata.items():
|
for table_name, table_rows in table_metadata.items():
|
||||||
|
@ -265,6 +267,25 @@ class DatabaseView(BaseView):
|
||||||
'database_hash': hash,
|
'database_hash': hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def custom_sql(self, request, name, hash):
|
||||||
|
params = request.raw_args
|
||||||
|
sql = params.pop('sql')
|
||||||
|
validate_sql_select(sql)
|
||||||
|
rows = await self.execute(name, sql, params)
|
||||||
|
columns = [r[0] for r in rows.description]
|
||||||
|
return {
|
||||||
|
'database': name,
|
||||||
|
'rows': rows,
|
||||||
|
'columns': columns,
|
||||||
|
'query': {
|
||||||
|
'sql': sql,
|
||||||
|
'params': params,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'database_hash': hash,
|
||||||
|
'custom_sql': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DatabaseDownload(BaseView):
|
class DatabaseDownload(BaseView):
|
||||||
async def view_get(self, request, name, hash, **kwargs):
|
async def view_get(self, request, name, hash, **kwargs):
|
||||||
|
|
|
@ -73,3 +73,34 @@ th {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.sql textarea {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
width: 70%;
|
||||||
|
height: 3em;
|
||||||
|
padding: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 576px) {
|
||||||
|
form.sql textarea {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form.sql p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
form.sql input[type=submit] {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #007bff;
|
||||||
|
border-color: #007bff;
|
||||||
|
font-weight: 400;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
border: 1px solid blue;
|
||||||
|
padding: .275rem .75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
border-radius: .25rem;
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% block title %}{{ database }}{% endblock %}
|
{% block title %}{{ database }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="hd"><a href="/">home</a></div>
|
<div class="hd"><a href="/">home</a>{% if query %} / <a href="/{{ database }}-{{ database_hash }}">{{ database }}</a>{% endif %}</div>
|
||||||
|
|
||||||
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_hash[:6] }}">{{ database }}</h1>
|
<h1 style="padding-left: 10px; border-left: 10px solid #{{ database_hash[:6] }}">{{ database }}</h1>
|
||||||
|
|
||||||
|
@ -13,6 +13,11 @@
|
||||||
<p><a href="/{{ database }}-{{ database_hash }}.db">download {{ database }}.db</a></p>
|
<p><a href="/{{ database }}-{{ database_hash }}.db">download {{ database }}.db</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<form class="sql" action="/{{ database }}-{{ database_hash }}" method="get">
|
||||||
|
<p><textarea name="sql">{% if query and query.sql %}{{ query.sql }}{% else %}select * from {{ tables[0].name }}{% endif %}</textarea></p>
|
||||||
|
<p><input type="submit" value="Run SQL"></p>
|
||||||
|
</form>
|
||||||
|
|
||||||
{% for table in tables %}
|
{% for table in tables %}
|
||||||
<div class="db-table">
|
<div class="db-table">
|
||||||
<h2><a href="/{{ database }}-{{ database_hash }}/{{ table.name }}">{{ table.name }}</a></h2>
|
<h2><a href="/{{ database }}-{{ database_hash }}/{{ table.name }}">{{ table.name }}</a></h2>
|
||||||
|
|
|
@ -34,6 +34,40 @@ def test_database_page(three_table_app_client):
|
||||||
assert response.status == 302
|
assert response.status == 302
|
||||||
_, response = three_table_app_client.get('/three_tables')
|
_, response = three_table_app_client.get('/three_tables')
|
||||||
assert 'three_tables' in response.text
|
assert 'three_tables' in response.text
|
||||||
|
# Test JSON list of tables
|
||||||
|
_, response = three_table_app_client.get('/three_tables.json')
|
||||||
|
data = response.json
|
||||||
|
assert 'three_tables' == data['database']
|
||||||
|
assert [{
|
||||||
|
'columns': ['pk1', 'pk2', 'content'],
|
||||||
|
'name': 'compound_primary_key',
|
||||||
|
'table_rows': 0
|
||||||
|
}, {
|
||||||
|
'columns': ['content'],
|
||||||
|
'name': 'no_primary_key',
|
||||||
|
'table_rows': 0,
|
||||||
|
}, {
|
||||||
|
'columns': ['pk', 'content'],
|
||||||
|
'name': 'simple_primary_key',
|
||||||
|
'table_rows': 2
|
||||||
|
}] == data['tables']
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_sql(three_table_app_client):
|
||||||
|
_, response = three_table_app_client.get(
|
||||||
|
'/three_tables.jsono?sql=select+content+from+simple_primary_key'
|
||||||
|
)
|
||||||
|
data = response.json
|
||||||
|
assert {
|
||||||
|
'sql': 'select content from simple_primary_key',
|
||||||
|
'params': {}
|
||||||
|
} == data['query']
|
||||||
|
assert [
|
||||||
|
{'content': 'hello'},
|
||||||
|
{'content': 'world'}
|
||||||
|
] == data['rows']
|
||||||
|
assert ['content'] == data['columns']
|
||||||
|
assert 'three_tables' == data['database']
|
||||||
|
|
||||||
|
|
||||||
def test_table_page(three_table_app_client):
|
def test_table_page(three_table_app_client):
|
||||||
|
|
Ładowanie…
Reference in New Issue