mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
parent
f59c840e7d
commit
eed6a0fe36
5 changed files with 75 additions and 5 deletions
|
|
@ -19,6 +19,7 @@ from .utils import (
|
||||||
build_where_clauses,
|
build_where_clauses,
|
||||||
compound_pks_from_path,
|
compound_pks_from_path,
|
||||||
CustomJSONEncoder,
|
CustomJSONEncoder,
|
||||||
|
detect_fts_sql,
|
||||||
escape_css_string,
|
escape_css_string,
|
||||||
escape_sqlite_table_name,
|
escape_sqlite_table_name,
|
||||||
get_all_foreign_keys,
|
get_all_foreign_keys,
|
||||||
|
|
@ -427,6 +428,22 @@ class TableView(BaseView):
|
||||||
where_clauses = []
|
where_clauses = []
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
|
# _search support:
|
||||||
|
fts_table = None
|
||||||
|
fts_sql = detect_fts_sql(table)
|
||||||
|
fts_rows = list(await self.execute(name, fts_sql))
|
||||||
|
if fts_rows:
|
||||||
|
fts_table=fts_rows[0][0]
|
||||||
|
|
||||||
|
search = special_args.get('_search')
|
||||||
|
if search and fts_table:
|
||||||
|
where_clauses.append(
|
||||||
|
'rowid in (select rowid from {fts_table} where {fts_table} match :search)'.format(
|
||||||
|
fts_table=fts_table
|
||||||
|
)
|
||||||
|
)
|
||||||
|
params['search'] = search
|
||||||
|
|
||||||
next = special_args.get('_next')
|
next = special_args.get('_next')
|
||||||
offset = ''
|
offset = ''
|
||||||
if next:
|
if next:
|
||||||
|
|
@ -504,6 +521,8 @@ class TableView(BaseView):
|
||||||
async def extra_template():
|
async def extra_template():
|
||||||
return {
|
return {
|
||||||
'database_hash': hash,
|
'database_hash': hash,
|
||||||
|
'supports_search': bool(fts_table),
|
||||||
|
'search': search or '',
|
||||||
'use_rowid': use_rowid,
|
'use_rowid': use_rowid,
|
||||||
'display_columns': display_columns,
|
'display_columns': display_columns,
|
||||||
'display_rows': await self.make_display_rows(name, hash, table, rows, display_columns, pks, is_view, use_rowid),
|
'display_rows': await self.make_display_rows(name, hash, table, rows, display_columns, pks, is_view, use_rowid),
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,13 @@ form.sql textarea {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
form.sql label {
|
form label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 15%;
|
width: 15%;
|
||||||
}
|
}
|
||||||
form.sql input[type=text] {
|
form input[type=text],
|
||||||
|
form input[type=search] {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
width: 60%;
|
width: 60%;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
@ -103,12 +104,15 @@ form.sql input[type=text] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
form input[type=search] {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
@media only screen and (max-width: 576px) {
|
@media only screen and (max-width: 576px) {
|
||||||
form.sql textarea {
|
form.sql textarea {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
form.sql input[type=submit] {
|
form input[type=submit] {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #007bff;
|
background-color: #007bff;
|
||||||
border-color: #007bff;
|
border-color: #007bff;
|
||||||
|
|
@ -118,7 +122,7 @@ form.sql input[type=submit] {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
border: 1px solid blue;
|
border: 1px solid blue;
|
||||||
padding: .275rem .75rem;
|
padding: .275rem .75rem;
|
||||||
font-size: 1rem;
|
font-size: 0.9rem;
|
||||||
line-height: 1.5;
|
line-height: 1;
|
||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@
|
||||||
<h2>{{ "{:,}".format(table_rows) }} total row{% if table_rows == 1 %}{% else %}s{% endif %} in this table</h2>
|
<h2>{{ "{:,}".format(table_rows) }} total row{% if table_rows == 1 %}{% else %}s{% endif %} in this table</h2>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if supports_search %}
|
||||||
|
<form action="/{{ database }}-{{ database_hash }}/{{ table|quote_plus }}" method="get">
|
||||||
|
<p><input type="search" name="_search" value="{{ search }}"> <input type="submit" value="Search"></p>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if query.params %}
|
{% if query.params %}
|
||||||
<pre>{{ query.sql }}</pre>
|
<pre>{{ query.sql }}</pre>
|
||||||
<pre>params = {{ query.params|tojson(4) }}</pre>
|
<pre>params = {{ query.params|tojson(4) }}</pre>
|
||||||
|
|
|
||||||
|
|
@ -235,3 +235,20 @@ def get_all_foreign_keys(conn):
|
||||||
})
|
})
|
||||||
|
|
||||||
return table_to_foreign_keys
|
return table_to_foreign_keys
|
||||||
|
|
||||||
|
|
||||||
|
def detect_fts(conn, table, return_sql=False):
|
||||||
|
"Detect if table has a corresponding FTS virtual table and return it"
|
||||||
|
rows = conn.execute(detect_fts_sql(table)).fetchall()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return rows[0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def detect_fts_sql(table):
|
||||||
|
return r'''
|
||||||
|
select name from sqlite_master
|
||||||
|
where rootpage = 0
|
||||||
|
and sql like '%VIRTUAL TABLE%USING FTS%content="{}"%';
|
||||||
|
'''.format(table)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ Tests for various datasette helper functions.
|
||||||
|
|
||||||
from datasette import utils
|
from datasette import utils
|
||||||
import pytest
|
import pytest
|
||||||
|
import sqlite3
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -124,3 +125,26 @@ def test_validate_sql_select_bad(bad_sql):
|
||||||
])
|
])
|
||||||
def test_validate_sql_select_good(good_sql):
|
def test_validate_sql_select_good(good_sql):
|
||||||
utils.validate_sql_select(good_sql)
|
utils.validate_sql_select(good_sql)
|
||||||
|
|
||||||
|
|
||||||
|
def test_detect_fts():
|
||||||
|
sql = '''
|
||||||
|
CREATE TABLE "Dumb_Table" (
|
||||||
|
"TreeID" INTEGER,
|
||||||
|
"qSpecies" TEXT
|
||||||
|
);
|
||||||
|
CREATE TABLE "Street_Tree_List" (
|
||||||
|
"TreeID" INTEGER,
|
||||||
|
"qSpecies" TEXT,
|
||||||
|
"qAddress" TEXT,
|
||||||
|
"SiteOrder" INTEGER,
|
||||||
|
"qSiteInfo" TEXT,
|
||||||
|
"PlantType" TEXT,
|
||||||
|
"qCaretaker" TEXT
|
||||||
|
);
|
||||||
|
CREATE VIRTUAL TABLE "Street_Tree_List_fts" USING FTS4 ("qAddress", "qCaretaker", "qSpecies", content="Street_Tree_List");
|
||||||
|
'''
|
||||||
|
conn = sqlite3.connect(':memory:')
|
||||||
|
conn.executescript(sql)
|
||||||
|
assert None is utils.detect_fts(conn, 'Dumb_Table')
|
||||||
|
assert 'Street_Tree_List_fts' == utils.detect_fts(conn, 'Street_Tree_List')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue