{"html_url": "https://github.com/simonw/datasette/issues/1518#issuecomment-997472214", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1518", "id": 997472214, "node_id": "IC_kwDOBm6k_c47dDfW", "user": {"value": 9599, "label": "simonw"}, "created_at": "2021-12-19T22:22:08Z", "updated_at": "2021-12-19T22:22:08Z", "author_association": "OWNER", "body": "I sketched out a chained SQL builder pattern that might be useful for further tidying up this code - though with the new plugin hook I'm less excited about it than I was:\r\n\r\n```python\r\nclass TableQuery:\r\n def __init__(self, table, columns, pks, is_view=False, prev=None):\r\n self.table = table\r\n self.columns = columns\r\n self.pks = pks\r\n self.is_view = is_view\r\n self.prev = prev\r\n \r\n # These can be changed for different instances in the chain:\r\n self._where_clauses = None\r\n self._order_by = None\r\n self._page_size = None\r\n self._offset = None\r\n self._select_columns = None\r\n\r\n self.select_all_columns = '*'\r\n self.select_specified_columns = '*'\r\n\r\n @property\r\n def where_clauses(self):\r\n wheres = []\r\n current = self\r\n while current:\r\n if current._where_clauses is not None:\r\n wheres.extend(current._where_clauses)\r\n current = current.prev\r\n return list(reversed(wheres))\r\n\r\n def where(self, where):\r\n new_cls = TableQuery(self.table, self.columns, self.pks, self.is_view, self)\r\n new_cls._where_clauses = [where]\r\n return new_cls\r\n \r\n @classmethod\r\n async def introspect(cls, db, table):\r\n return cls(\r\n table,\r\n columns = await db.table_columns(table),\r\n pks = await db.primary_keys(table),\r\n is_view = bool(await db.get_view_definition(table))\r\n )\r\n \r\n @property\r\n def sql_from(self):\r\n return f\"from {self.table}{self.sql_where}\"\r\n\r\n @property\r\n def sql_where(self):\r\n if not self.where_clauses:\r\n return \"\"\r\n else:\r\n return f\" where {' and '.join(self.where_clauses)}\"\r\n\r\n @property\r\n def sql_no_order_no_limit(self):\r\n return f\"select {self.select_all_columns} from {self.table}{self.sql_where}\"\r\n\r\n @property\r\n def sql(self):\r\n return f\"select {self.select_specified_columns} from {self.table} {self.sql_where}{self._order_by} limit {self._page_size}{self._offset}\"\r\n\r\n @property\r\n def sql_count(self):\r\n return f\"select count(*) {self.sql_from}\"\r\n\r\n\r\n def __repr__(self):\r\n return f\"\"\r\n```\r\nUsage:\r\n```python\r\nfrom datasette.app import Datasette\r\nds = Datasette(memory=True, files=[\"/Users/simon/Dropbox/Development/datasette/fixtures.db\"])\r\ndb = ds.get_database(\"fixtures\")\r\nquery = await TableQuery.introspect(db, \"facetable\")\r\nprint(query.where(\"foo = bar\").where(\"baz = 1\").sql_count)\r\n# 'select count(*) from facetable where foo = bar and baz = 1'\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1058072543, "label": "Complete refactor of TableView and table.html template"}, "performed_via_github_app": null}