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