mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Refactor to use new resolve_database/table/row methods, refs #1896
This commit is contained in:
parent
c588a89f26
commit
ee64130fa8
8 changed files with 254 additions and 133 deletions
|
|
@ -20,7 +20,6 @@ from datasette.utils import (
|
|||
InvalidSql,
|
||||
LimitedWriter,
|
||||
call_with_supported_arguments,
|
||||
tilde_decode,
|
||||
path_from_row_pks,
|
||||
path_with_added_args,
|
||||
path_with_removed_args,
|
||||
|
|
@ -346,13 +345,9 @@ class DataView(BaseView):
|
|||
return AsgiStream(stream_fn, headers=headers, content_type=content_type)
|
||||
|
||||
async def get(self, request):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
raise NotFound("Database not found: {}".format(database_route))
|
||||
db = await self.ds.resolve_database(request)
|
||||
database = db.name
|
||||
database_route = db.route
|
||||
|
||||
_format = request.url_vars["format"]
|
||||
data_kwargs = {}
|
||||
|
|
|
|||
|
|
@ -35,11 +35,7 @@ class DatabaseView(DataView):
|
|||
name = "database"
|
||||
|
||||
async def data(self, request, default_labels=False, _size=None):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
raise NotFound("Database not found: {}".format(database_route))
|
||||
db = await self.ds.resolve_database(request)
|
||||
database = db.name
|
||||
|
||||
visible, private = await self.ds.check_visibility(
|
||||
|
|
@ -228,11 +224,7 @@ class QueryView(DataView):
|
|||
named_parameters=None,
|
||||
write=False,
|
||||
):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
raise NotFound("Database not found: {}".format(database_route))
|
||||
db = await self.ds.resolve_database(request)
|
||||
database = db.name
|
||||
params = {key: request.args.get(key) for key in request.args}
|
||||
if "sql" in params:
|
||||
|
|
@ -582,11 +574,7 @@ class TableCreateView(BaseView):
|
|||
self.ds = datasette
|
||||
|
||||
async def post(self, request):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
return _error(["Database not found: {}".format(database_route)], 404)
|
||||
db = await self.ds.resolve_database(request)
|
||||
database_name = db.name
|
||||
|
||||
# Must have create-table permission
|
||||
|
|
@ -727,11 +715,7 @@ class TableCreateView(BaseView):
|
|||
self.ds = datasette
|
||||
|
||||
async def post(self, request):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
return _error(["Database not found: {}".format(database_route)], 404)
|
||||
db = await self.ds.resolve_database(request)
|
||||
database_name = db.name
|
||||
|
||||
# Must have create-table permission
|
||||
|
|
|
|||
|
|
@ -6,22 +6,21 @@ from datasette.utils import (
|
|||
urlsafe_components,
|
||||
to_css_class,
|
||||
escape_sqlite,
|
||||
row_sql_params_pks,
|
||||
)
|
||||
import json
|
||||
import sqlite_utils
|
||||
from .table import _sql_params_pks, display_columns_and_rows
|
||||
from .table import display_columns_and_rows
|
||||
|
||||
|
||||
class RowView(DataView):
|
||||
name = "row"
|
||||
|
||||
async def data(self, request, default_labels=False):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
table = tilde_decode(request.url_vars["table"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
raise NotFound("Database not found: {}".format(database_route))
|
||||
database = db.name
|
||||
resolved = await self.ds.resolve_row(request)
|
||||
database = resolved.db.name
|
||||
table = resolved.table
|
||||
pk_values = resolved.pk_values
|
||||
|
||||
# Ensure user has permission to view this row
|
||||
visible, private = await self.ds.check_visibility(
|
||||
|
|
@ -35,14 +34,9 @@ class RowView(DataView):
|
|||
if not visible:
|
||||
raise Forbidden("You do not have permission to view this table")
|
||||
|
||||
pk_values = urlsafe_components(request.url_vars["pks"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
raise NotFound("Database not found: {}".format(database_route))
|
||||
database = db.name
|
||||
sql, params, pks = await _sql_params_pks(db, table, pk_values)
|
||||
results = await db.execute(sql, params, truncate=True)
|
||||
results = await resolved.db.execute(
|
||||
resolved.sql, resolved.params, truncate=True
|
||||
)
|
||||
columns = [r[0] for r in results.description]
|
||||
rows = list(results.rows)
|
||||
if not rows:
|
||||
|
|
@ -83,7 +77,7 @@ class RowView(DataView):
|
|||
"table": table,
|
||||
"rows": rows,
|
||||
"columns": columns,
|
||||
"primary_keys": pks,
|
||||
"primary_keys": resolved.pks,
|
||||
"primary_key_values": pk_values,
|
||||
"units": self.ds.table_metadata(database, table).get("units", {}),
|
||||
}
|
||||
|
|
@ -149,6 +143,11 @@ class RowView(DataView):
|
|||
return foreign_key_tables
|
||||
|
||||
|
||||
class RowError(Exception):
|
||||
def __init__(self, error):
|
||||
self.error = error
|
||||
|
||||
|
||||
class RowDeleteView(BaseView):
|
||||
name = "row-delete"
|
||||
|
||||
|
|
@ -156,24 +155,20 @@ class RowDeleteView(BaseView):
|
|||
self.ds = datasette
|
||||
|
||||
async def post(self, request):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
table = tilde_decode(request.url_vars["table"])
|
||||
from datasette.app import DatabaseNotFound, TableNotFound, RowNotFound
|
||||
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
return _error(["Database not found: {}".format(database_route)], 404)
|
||||
|
||||
resolved = await self.ds.resolve_row(request)
|
||||
except DatabaseNotFound as e:
|
||||
return _error(["Database not found: {}".format(e.database_name)], 404)
|
||||
except TableNotFound as e:
|
||||
return _error(["Table not found: {}".format(e.table)], 404)
|
||||
except RowNotFound as e:
|
||||
return _error(["Record not found: {}".format(e.pk_values)], 404)
|
||||
db = resolved.db
|
||||
database_name = db.name
|
||||
if not await db.table_exists(table):
|
||||
return _error(["Table not found: {}".format(table)], 404)
|
||||
|
||||
pk_values = urlsafe_components(request.url_vars["pks"])
|
||||
|
||||
sql, params, pks = await _sql_params_pks(db, table, pk_values)
|
||||
results = await db.execute(sql, params, truncate=True)
|
||||
rows = list(results.rows)
|
||||
if not rows:
|
||||
return _error([f"Record not found: {pk_values}"], 404)
|
||||
table = resolved.table
|
||||
pk_values = resolved.pk_values
|
||||
|
||||
# Ensure user has permission to delete this row
|
||||
if not await self.ds.permission_allowed(
|
||||
|
|
|
|||
|
|
@ -93,36 +93,33 @@ class TableView(DataView):
|
|||
return expandables
|
||||
|
||||
async def post(self, request):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
raise NotFound("Database not found: {}".format(database_route))
|
||||
database_name = db.name
|
||||
table_name = tilde_decode(request.url_vars["table"])
|
||||
# Handle POST to a canned query
|
||||
canned_query = await self.ds.get_canned_query(
|
||||
database_name, table_name, request.actor
|
||||
)
|
||||
if canned_query:
|
||||
return await QueryView(self.ds).data(
|
||||
request,
|
||||
canned_query["sql"],
|
||||
metadata=canned_query,
|
||||
editable=False,
|
||||
canned_query=table_name,
|
||||
named_parameters=canned_query.get("params"),
|
||||
write=bool(canned_query.get("write")),
|
||||
)
|
||||
else:
|
||||
# Handle POST to a table
|
||||
return await self.table_post(request, database_name, table_name)
|
||||
from datasette.app import TableNotFound
|
||||
|
||||
async def table_post(self, request, database_name, table_name):
|
||||
# Table must exist (may handle table creation in the future)
|
||||
db = self.ds.get_database(database_name)
|
||||
if not await db.table_exists(table_name):
|
||||
raise NotFound("Table not found: {}".format(table_name))
|
||||
try:
|
||||
resolved = await self.ds.resolve_table(request)
|
||||
except TableNotFound as e:
|
||||
# Was this actually a canned query?
|
||||
canned_query = await self.ds.get_canned_query(
|
||||
e.database_name, e.table, request.actor
|
||||
)
|
||||
if canned_query:
|
||||
# Handle POST to a canned query
|
||||
return await QueryView(self.ds).data(
|
||||
request,
|
||||
canned_query["sql"],
|
||||
metadata=canned_query,
|
||||
editable=False,
|
||||
canned_query=e.table,
|
||||
named_parameters=canned_query.get("params"),
|
||||
write=bool(canned_query.get("write")),
|
||||
)
|
||||
|
||||
# Handle POST to a table
|
||||
return await self.table_post(
|
||||
request, resolved.db, resolved.db.name, resolved.table
|
||||
)
|
||||
|
||||
async def table_post(self, request, db, database_name, table_name):
|
||||
# Must have insert-row permission
|
||||
if not await self.ds.permission_allowed(
|
||||
request.actor, "insert-row", resource=(database_name, table_name)
|
||||
|
|
@ -221,12 +218,31 @@ class TableView(DataView):
|
|||
_next=None,
|
||||
_size=None,
|
||||
):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
table_name = tilde_decode(request.url_vars["table"])
|
||||
from datasette.app import TableNotFound
|
||||
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
raise NotFound("Database not found: {}".format(database_route))
|
||||
resolved = await self.ds.resolve_table(request)
|
||||
except TableNotFound as e:
|
||||
# Was this actually a canned query?
|
||||
canned_query = await self.ds.get_canned_query(
|
||||
e.database_name, e.table, request.actor
|
||||
)
|
||||
# If this is a canned query, not a table, then dispatch to QueryView instead
|
||||
if canned_query:
|
||||
return await QueryView(self.ds).data(
|
||||
request,
|
||||
canned_query["sql"],
|
||||
metadata=canned_query,
|
||||
editable=False,
|
||||
canned_query=e.table,
|
||||
named_parameters=canned_query.get("params"),
|
||||
write=bool(canned_query.get("write")),
|
||||
)
|
||||
else:
|
||||
raise
|
||||
|
||||
table_name = resolved.table
|
||||
db = resolved.db
|
||||
database_name = db.name
|
||||
|
||||
# For performance profiling purposes, ?_noparallel=1 turns off asyncio.gather
|
||||
|
|
@ -243,21 +259,6 @@ class TableView(DataView):
|
|||
_gather_sequential if request.args.get("_noparallel") else _gather_parallel
|
||||
)
|
||||
|
||||
# If this is a canned query, not a table, then dispatch to QueryView instead
|
||||
canned_query = await self.ds.get_canned_query(
|
||||
database_name, table_name, request.actor
|
||||
)
|
||||
if canned_query:
|
||||
return await QueryView(self.ds).data(
|
||||
request,
|
||||
canned_query["sql"],
|
||||
metadata=canned_query,
|
||||
editable=False,
|
||||
canned_query=table_name,
|
||||
named_parameters=canned_query.get("params"),
|
||||
write=bool(canned_query.get("write")),
|
||||
)
|
||||
|
||||
is_view, table_exists = map(
|
||||
bool,
|
||||
await gather(
|
||||
|
|
@ -874,21 +875,6 @@ class TableView(DataView):
|
|||
)
|
||||
|
||||
|
||||
async def _sql_params_pks(db, table, pk_values):
|
||||
pks = await db.primary_keys(table)
|
||||
use_rowid = not pks
|
||||
select = "*"
|
||||
if use_rowid:
|
||||
select = "rowid, *"
|
||||
pks = ["rowid"]
|
||||
wheres = [f'"{pk}"=:p{i}' for i, pk in enumerate(pks)]
|
||||
sql = f"select {select} from {escape_sqlite(table)} where {' AND '.join(wheres)}"
|
||||
params = {}
|
||||
for i, pk_value in enumerate(pk_values):
|
||||
params[f"p{i}"] = pk_value
|
||||
return sql, params, pks
|
||||
|
||||
|
||||
async def display_columns_and_rows(
|
||||
datasette,
|
||||
database_name,
|
||||
|
|
@ -1161,13 +1147,13 @@ class TableInsertView(BaseView):
|
|||
return rows, errors, extras
|
||||
|
||||
async def post(self, request):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
return _error(["Database not found: {}".format(database_route)], 404)
|
||||
resolved = await self.ds.resolve_table(request)
|
||||
except NotFound as e:
|
||||
return _error([e.args[0]], 404)
|
||||
db = resolved.db
|
||||
database_name = db.name
|
||||
table_name = tilde_decode(request.url_vars["table"])
|
||||
table_name = resolved.table
|
||||
|
||||
# Table must exist (may handle table creation in the future)
|
||||
db = self.ds.get_database(database_name)
|
||||
|
|
@ -1221,13 +1207,13 @@ class TableDropView(BaseView):
|
|||
self.ds = datasette
|
||||
|
||||
async def post(self, request):
|
||||
database_route = tilde_decode(request.url_vars["database"])
|
||||
try:
|
||||
db = self.ds.get_database(route=database_route)
|
||||
except KeyError:
|
||||
return _error(["Database not found: {}".format(database_route)], 404)
|
||||
resolved = await self.ds.resolve_table(request)
|
||||
except NotFound as e:
|
||||
return _error([e.args[0]], 404)
|
||||
db = resolved.db
|
||||
database_name = db.name
|
||||
table_name = tilde_decode(request.url_vars["table"])
|
||||
table_name = resolved.table
|
||||
# Table must exist
|
||||
db = self.ds.get_database(database_name)
|
||||
if not await db.table_exists(table_name):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue