diff --git a/datasette/app.py b/datasette/app.py index df33386b..d7dace67 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -1,5 +1,4 @@ import asyncio -from pydoc import plain from typing import Sequence, Union, Tuple, Optional, Dict, Iterable import asgi_csrf import collections @@ -24,7 +23,12 @@ from pathlib import Path from markupsafe import Markup, escape from itsdangerous import URLSafeSerializer -from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader +from jinja2 import ( + ChoiceLoader, + Environment, + FileSystemLoader, + PrefixLoader, +) from jinja2.environment import Template from jinja2.exceptions import TemplateNotFound @@ -42,7 +46,12 @@ from .views.special import ( PermissionsDebugView, MessagesDebugView, ) -from .views.table import TableView, TableInsertView, TableUpsertView, TableDropView +from .views.table import ( + TableInsertView, + TableUpsertView, + TableDropView, + table_view, +) from .views.row import RowView, RowDeleteView, RowUpdateView from .renderer import json_renderer from .url_builder import Urls @@ -389,7 +398,10 @@ class Datasette: ] ) self.jinja_env = Environment( - loader=template_loader, autoescape=True, enable_async=True + loader=template_loader, + autoescape=True, + enable_async=True, + # undefined=StrictUndefined, ) self.jinja_env.filters["escape_css_string"] = escape_css_string self.jinja_env.filters["quote_plus"] = urllib.parse.quote_plus @@ -1358,7 +1370,7 @@ class Datasette: ) add_route(TableCreateView.as_view(self), r"/(?P[^\/\.]+)/-/create$") add_route( - TableView.as_view(self), + wrap_view(table_view, self), r"/(?P[^\/\.]+)/(?P[^\/\.]+)(\.(?P\w+))?$", ) add_route( diff --git a/datasette/cli.py b/datasette/cli.py index a3ae1269..a6de9e6d 100644 --- a/datasette/cli.py +++ b/datasette/cli.py @@ -136,6 +136,7 @@ def sqlite_extensions(fn): multiple=True, help="Path to a SQLite extension to load, and optional entrypoint", )(fn) + # Wrap it in a custom error handler @functools.wraps(fn) def wrapped(*args, **kwargs): diff --git a/datasette/renderer.py b/datasette/renderer.py index 16990efa..5354f348 100644 --- a/datasette/renderer.py +++ b/datasette/renderer.py @@ -4,6 +4,7 @@ from datasette.utils import ( remove_infinites, CustomJSONEncoder, path_from_row_pks, + sqlite3, ) from datasette.utils.asgi import Response @@ -49,10 +50,14 @@ def json_renderer(args, data, view_name): if data.get("error"): shape = "objects" - next_url = data.get("next_url") - if shape == "arrayfirst": - data = [row[0] for row in data["rows"]] + if not data["rows"]: + data = [] + elif isinstance(data["rows"][0], sqlite3.Row): + data = [row[0] for row in data["rows"]] + else: + assert isinstance(data["rows"][0], dict) + data = [next(iter(row.values())) for row in data["rows"]] elif shape in ("objects", "object", "array"): columns = data.get("columns") rows = data.get("rows") @@ -80,7 +85,12 @@ def json_renderer(args, data, view_name): data = data["rows"] elif shape == "arrays": - pass + if not data["rows"]: + pass + elif isinstance(data["rows"][0], sqlite3.Row): + data["rows"] = [list(row) for row in data["rows"]] + else: + data["rows"] = [list(row.values()) for row in data["rows"]] else: status_code = 400 data = { @@ -98,8 +108,6 @@ def json_renderer(args, data, view_name): body = json.dumps(data, cls=CustomJSONEncoder) content_type = "application/json; charset=utf-8" headers = {} - if next_url: - headers["link"] = f'<{next_url}>; rel="next"' return Response( body, status=status_code, headers=headers, content_type=content_type ) diff --git a/datasette/templates/_description_source_license.html b/datasette/templates/_description_source_license.html index a2bc18f2..f852268f 100644 --- a/datasette/templates/_description_source_license.html +++ b/datasette/templates/_description_source_license.html @@ -1,6 +1,6 @@ -{% if metadata.description_html or metadata.description %} +{% if metadata.get("description_html") or metadata.get("description") %}