{"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106908642", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106908642, "node_id": "IC_kwDOBm6k_c5B-hXi", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T21:47:55Z", "updated_at": "2022-04-22T21:47:55Z", "author_association": "OWNER", "body": "I need a `asyncio.Registry` with functions registered to perform the role of the table view.\r\n\r\nSomething like this perhaps:\r\n```python\r\ndef table_html_context(facet_results, query, datasette, rows):\r\n return {...}\r\n```\r\nThat then gets called like this:\r\n```python\r\nasync def view(request):\r\n registry = Registry(facet_results, query, datasette, rows)\r\n context = await registry.resolve(table_html, request=request, datasette=datasette)\r\n return Reponse.html(await datasette.render(\"table.html\", context)\r\n```\r\nIt's also interesting to start thinking about this from a Python client library point of view. If I'm writing code outside of the HTTP request cycle, what would it look like?\r\n\r\nOne thing I could do: break out is the code that turns a request into a list of pairs extracted from the request - this code here: https://github.com/simonw/datasette/blob/8338c66a57502ef27c3d7afb2527fbc0663b2570/datasette/views/table.py#L442-L449\r\n\r\nI could turn that into a typed dependency injection function like this:\r\n\r\n```python\r\ndef filter_args(request: Request) -> List[Tuple[str, str]]:\r\n # Arguments that start with _ and don't contain a __ are\r\n # special - things like ?_search= - and should not be\r\n # treated as filters.\r\n filter_args = []\r\n for key in request.args:\r\n if not (key.startswith(\"_\") and \"__\" not in key):\r\n for v in request.args.getlist(key):\r\n filter_args.append((key, v))\r\n return filter_args\r\n```\r\nThen I can either pass a `request` into a `.resolve()` call, or I can instead skip that function by passing:\r\n\r\n```python\r\noutput = registry.resolve(table_context, filter_args=[(\"foo\", \"bar\")])\r\n```\r\nI do need to think about where plugins get executed in all of this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1716#issuecomment-1106923258", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1716", "id": 1106923258, "node_id": "IC_kwDOBm6k_c5B-k76", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T22:02:07Z", "updated_at": "2022-04-22T22:02:07Z", "author_association": "OWNER", "body": "https://github.com/simonw/datasette/blame/main/datasette/views/base.py\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212838949, "label": "Configure git blame to ignore Black commit"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106945876", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106945876, "node_id": "IC_kwDOBm6k_c5B-qdU", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T22:24:29Z", "updated_at": "2022-04-22T22:24:29Z", "author_association": "OWNER", "body": "Looking at the start of `TableView.data()`:\r\n\r\nhttps://github.com/simonw/datasette/blob/d57c347f35bcd8cff15f913da851b4b8eb030867/datasette/views/table.py#L333-L346\r\n\r\nI'm going to resolve `table_name` and `database` from the URL - `table_name` will be a string, `database` will be the DB object returned by `datasette.get_database()`. Then those can be passed in separately too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106947168", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106947168, "node_id": "IC_kwDOBm6k_c5B-qxg", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T22:25:57Z", "updated_at": "2022-04-22T22:26:06Z", "author_association": "OWNER", "body": "```python\r\nasync def database(request: Request, datasette: Datasette) -> Database:\r\n database_route = tilde_decode(request.url_vars[\"database\"])\r\n try:\r\n return datasette.get_database(route=database_route)\r\n except KeyError:\r\n raise NotFound(\"Database not found: {}\".format(database_route))\r\n\r\nasync def table_name(request: Request) -> str:\r\n return tilde_decode(request.url_vars[\"table\"])\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106989581", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106989581, "node_id": "IC_kwDOBm6k_c5B-1IN", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T23:03:29Z", "updated_at": "2022-04-22T23:03:29Z", "author_association": "OWNER", "body": "I'm having second thoughts about injecting `request` - might be better to have the view function pull the relevant pieces out of the request before triggering the rest of the resolution.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null}