html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,issue,performed_via_github_app https://github.com/simonw/datasette/issues/1195#issuecomment-763108730,https://api.github.com/repos/simonw/datasette/issues/1195,763108730,MDEyOklzc3VlQ29tbWVudDc2MzEwODczMA==,9599,2021-01-19T20:22:37Z,2021-01-19T20:22:37Z,OWNER,I can use this test: https://github.com/simonw/datasette/blob/c38c42948cbfddd587729413fd6082ba352eaece/tests/test_plugins.py#L238-L294,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",789336592, https://github.com/simonw/datasette/issues/1195#issuecomment-766534634,https://api.github.com/repos/simonw/datasette/issues/1195,766534634,MDEyOklzc3VlQ29tbWVudDc2NjUzNDYzNA==,9599,2021-01-25T04:38:30Z,2021-01-25T04:38:30Z,OWNER,"This has proved surprisingly difficult to implement, due to the weird way the QueryView is actually called. The class itself isn't used like other view classes - instead, the `.data()` methods of both `DatabaseView` and `TableView` dispatch out to `QueryView.data()` when they need to: https://github.com/simonw/datasette/blob/07e163561592c743e4117f72102fcd350a600909/datasette/views/table.py#L259-L270 https://github.com/simonw/datasette/blob/07e163561592c743e4117f72102fcd350a600909/datasette/views/table.py#L290-L294 https://github.com/simonw/datasette/blob/07e163561592c743e4117f72102fcd350a600909/datasette/views/database.py#L39-L44 It turns out this is a bad pattern because it makes changes like this one WAY harder than they should be. I think I should clean this up as part of #878.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",789336592, https://github.com/simonw/datasette/issues/1195#issuecomment-766534748,https://api.github.com/repos/simonw/datasette/issues/1195,766534748,MDEyOklzc3VlQ29tbWVudDc2NjUzNDc0OA==,9599,2021-01-25T04:38:56Z,2021-01-25T04:38:56Z,OWNER,"Here's a diff showing how far I got before I abandoned this particular effort: ```diff diff --git a/datasette/views/base.py b/datasette/views/base.py index a21b929..04e4aa9 100644 --- a/datasette/views/base.py +++ b/datasette/views/base.py @@ -120,7 +120,7 @@ class BaseView: handler = getattr(self, request.method.lower(), None) return await handler(request, *args, **kwargs) - async def render(self, templates, request, context=None): + async def render(self, templates, request, context=None, view_name=None): context = context or {} template = self.ds.jinja_env.select_template(templates) template_context = { @@ -135,7 +135,7 @@ class BaseView: } return Response.html( await self.ds.render_template( - template, template_context, request=request, view_name=self.name + template, template_context, request=request, view_name=view_name ) ) @@ -155,7 +155,7 @@ class BaseView: class DataView(BaseView): - name = """" + view_name = ""no-view-name"" re_named_parameter = re.compile("":([a-zA-Z0-9_]+)"") async def options(self, request, *args, **kwargs): @@ -414,6 +414,10 @@ class DataView(BaseView): args[""table""] = urllib.parse.unquote_plus(args[""table""]) return _format, args + async def get_view_name(self, request, database, hash, **kwargs): + # Defaults to self.view_name, but can be over-ridden by subclasses + return self.view_name + async def view_get(self, request, database, hash, correct_hash_provided, **kwargs): _format, kwargs = await self.get_format(request, database, kwargs) @@ -424,6 +428,8 @@ class DataView(BaseView): # HTML views default to expanding all foreign key labels kwargs[""default_labels""] = True + view_name = await self.get_view_name(request, database, hash, **kwargs) + extra_template_data = {} start = time.perf_counter() status_code = 200 @@ -489,7 +495,7 @@ class DataView(BaseView): database=database, table=data.get(""table""), request=request, - view_name=self.name, + view_name=view_name, # These will be deprecated in Datasette 1.0: args=request.args, data=data, @@ -533,7 +539,7 @@ class DataView(BaseView): database=database, table=data.get(""table""), request=request, - view_name=self.name, + view_name=view_name, ) it_can_render = await await_me_maybe(it_can_render) if it_can_render: @@ -565,7 +571,7 @@ class DataView(BaseView): } if ""metadata"" not in context: context[""metadata""] = self.ds.metadata - r = await self.render(templates, request=request, context=context) + r = await self.render(templates, request=request, context=context, view_name=view_name) r.status = status_code ttl = request.args.get(""_ttl"", None) diff --git a/datasette/views/database.py b/datasette/views/database.py index f6fd579..e425213 100644 --- a/datasette/views/database.py +++ b/datasette/views/database.py @@ -23,7 +23,11 @@ from .base import DatasetteError, DataView class DatabaseView(DataView): - name = ""database"" + async def get_view_name(self, request, db_name, table_and_format): + if request.args.get(""sql""): + return ""query"" + else: + return ""database"" async def data(self, request, database, hash, default_labels=False, _size=None): await self.check_permissions( @@ -145,7 +149,7 @@ class DatabaseView(DataView): class DatabaseDownload(DataView): - name = ""database_download"" + view_name = ""database_download"" async def view_get(self, request, database, hash, correct_hash_present, **kwargs): await self.check_permissions( diff --git a/datasette/views/index.py b/datasette/views/index.py index b6b8cbe..d750e3d 100644 --- a/datasette/views/index.py +++ b/datasette/views/index.py @@ -16,7 +16,7 @@ COUNT_DB_SIZE_LIMIT = 100 * 1024 * 1024 class IndexView(BaseView): - name = ""index"" + view_name = ""index"" async def get(self, request, as_format): await self.check_permission(request, ""view-instance"") diff --git a/datasette/views/special.py b/datasette/views/special.py index 9750dd0..dbd1e00 100644 --- a/datasette/views/special.py +++ b/datasette/views/special.py @@ -6,7 +6,7 @@ import secrets class JsonDataView(BaseView): - name = ""json_data"" + view_name = ""json_data"" def __init__(self, datasette, filename, data_callback, needs_request=False): self.ds = datasette @@ -42,7 +42,7 @@ class JsonDataView(BaseView): class PatternPortfolioView(BaseView): - name = ""patterns"" + view_name = ""patterns"" async def get(self, request): await self.check_permission(request, ""view-instance"") @@ -50,7 +50,7 @@ class PatternPortfolioView(BaseView): class AuthTokenView(BaseView): - name = ""auth_token"" + view_name = ""auth_token"" async def get(self, request): token = request.args.get(""token"") or """" @@ -68,7 +68,7 @@ class AuthTokenView(BaseView): class LogoutView(BaseView): - name = ""logout"" + view_name = ""logout"" async def get(self, request): if not request.actor: @@ -87,7 +87,7 @@ class LogoutView(BaseView): class PermissionsDebugView(BaseView): - name = ""permissions_debug"" + view_name = ""permissions_debug"" async def get(self, request): await self.check_permission(request, ""view-instance"") @@ -102,7 +102,7 @@ class PermissionsDebugView(BaseView): class AllowDebugView(BaseView): - name = ""allow_debug"" + view_name = ""allow_debug"" async def get(self, request): errors = [] @@ -136,7 +136,7 @@ class AllowDebugView(BaseView): class MessagesDebugView(BaseView): - name = ""messages_debug"" + view_name = ""messages_debug"" async def get(self, request): await self.check_permission(request, ""view-instance"") diff --git a/datasette/views/table.py b/datasette/views/table.py index 0a3504b..45d298a 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -257,7 +257,16 @@ class RowTableShared(DataView): class TableView(RowTableShared): - name = ""table"" + view_name = ""table"" + + async def get_view_name(self, request, db_name, table_and_format): + canned_query = await self.ds.get_canned_query( + db_name, table_and_format, request.actor + ) + if canned_query: + return ""query"" + else: + return ""table"" async def post(self, request, db_name, table_and_format): # Handle POST to a canned query @@ -923,7 +932,7 @@ async def _sql_params_pks(db, table, pk_values): class RowView(RowTableShared): - name = ""row"" + view_name = ""row"" async def data(self, request, database, hash, table, pk_path, default_labels=False): await self.check_permissions( diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 715c7c1..7ce2b1b 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -252,7 +252,7 @@ def test_plugin_config_file(app_client): }, ), ( - ""/fixtures/"", + ""/fixtures"", { ""template"": ""database.html"", ""database"": ""fixtures"", @@ -285,6 +285,38 @@ def test_plugin_config_file(app_client): ], }, ), + ( + ""/fixtures?sql=select+1+as+one"", + { + ""template"": ""query.html"", + ""database"": ""fixtures"", + ""table"": None, + ""config"": {""depth"": ""database""}, + ""view_name"": ""query"", + ""request_path"": ""/fixtures"", + ""added"": 15, + ""columns"": [ + ""one"", + ], + }, + ), + ( + ""/fixtures/neighborhood_search"", + { + ""template"": ""query.html"", + ""database"": ""fixtures"", + ""table"": None, + ""config"": {""depth"": ""database""}, + ""view_name"": ""query"", + ""request_path"": ""/fixtures/neighborhood_search"", + ""added"": 15, + ""columns"": [ + ""neighborhood"", + ""name"", + ""state"", + ], + }, + ), ], ) def test_hook_extra_body_script(app_client, path, expected_extra_body_script): ```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",789336592, https://github.com/simonw/datasette/issues/1195#issuecomment-766535046,https://api.github.com/repos/simonw/datasette/issues/1195,766535046,MDEyOklzc3VlQ29tbWVudDc2NjUzNTA0Ng==,9599,2021-01-25T04:40:08Z,2021-01-25T04:40:08Z,OWNER,"Also: should the view name really be the same for both of these pages? - https://latest.datasette.io/fixtures?sql=select+*+from+facetable - https://latest.datasette.io/fixtures/neighborhood_search Where one of them is a canned query and the other is an arbitrary query?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",789336592,