From 0cf91e3f8822476eedc8a8703ca1b729a94e9a1a Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 30 Jul 2018 08:16:56 -0700 Subject: [PATCH 1/3] Started playing with m2m options This query is really slow. I think this pattern would be faster: select ads.id, ads.text, ads.url from ads inner join ad_targets ad_targets_1 on ad_targets_1.ad_id = ads.id inner join ad_targets ad_targets_2 on ad_targets_2.ad_id = ads.id where ad_targets_1.target_id = "cd0d0" and ad_targets_2.target_id = "26eb3" order by ads.id --- datasette/views/table.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/datasette/views/table.py b/datasette/views/table.py index 654e60fa..9e9be31a 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -269,7 +269,7 @@ class TableView(RowTableShared): special_args_lists = {} other_args = {} for key, value in args.items(): - if key.startswith("_") and "__" not in key: + if (key.startswith("_") and "__" not in key) or key.startswith("_m2m_"): special_args[key] = value[0] special_args_lists[key] = value else: @@ -304,6 +304,30 @@ class TableView(RowTableShared): filters = Filters(sorted(other_args.items()), units, ureg) where_clauses, params = filters.build_where_clauses() + # Hacky thing for ?_m2m_ad_targets__target_id=9a8c6 + for m2m_key in special_args: + if m2m_key.startswith("_m2m_"): + rest = m2m_key.split("_m2m_", 1)[1] + m2m_table, other_column = rest.split("__", 1) + m2m_table_info = self.ds.inspect()[name]["tables"][m2m_table] + outgoing_fks = m2m_table_info["foreign_keys"]["outgoing"] + fk_to_us = [fk for fk in outgoing_fks if fk["other_table"] == table][0] + # fk_to_other = [ + # fk for fk in outgoing_fks + # if fk["other_table"] != table + # and fk["other_column"] == other_column + # ][0] + value = special_args[m2m_key] + # Figure out what the columns are in that m2m table + where_clauses.append( + '{our_pk} in (select {our_pk} from {m2m_table} where {other_column} = "{value}")'.format( + m2m_table=escape_sqlite(m2m_table), + our_pk=fk_to_us["other_column"], + other_column=escape_sqlite(other_column), + value=value, + ) + ) + # _search support: fts_table = info[name]["tables"].get(table, {}).get("fts_table") search_args = dict( From 04c7fd62077e7daffdaa92f00a2e4e51cbbad3a1 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 30 Jul 2018 20:54:38 -0700 Subject: [PATCH 2/3] Handle multiple m2m args of same name e.g. ?_m2m_ad_targets__target_id=ec3ac&_m2m_ad_targets__target_id=e128e --- datasette/views/table.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/datasette/views/table.py b/datasette/views/table.py index 9e9be31a..6562bdaa 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -305,7 +305,7 @@ class TableView(RowTableShared): where_clauses, params = filters.build_where_clauses() # Hacky thing for ?_m2m_ad_targets__target_id=9a8c6 - for m2m_key in special_args: + for m2m_key in request.args: if m2m_key.startswith("_m2m_"): rest = m2m_key.split("_m2m_", 1)[1] m2m_table, other_column = rest.split("__", 1) @@ -317,16 +317,17 @@ class TableView(RowTableShared): # if fk["other_table"] != table # and fk["other_column"] == other_column # ][0] - value = special_args[m2m_key] - # Figure out what the columns are in that m2m table - where_clauses.append( - '{our_pk} in (select {our_pk} from {m2m_table} where {other_column} = "{value}")'.format( - m2m_table=escape_sqlite(m2m_table), - our_pk=fk_to_us["other_column"], - other_column=escape_sqlite(other_column), - value=value, + for value in request.args[m2m_key]: + # Figure out what the columns are in that m2m table + where_clauses.append( + '{our_pk} in (select {our_column} from {m2m_table} where {other_column} = "{value}")'.format( + m2m_table=escape_sqlite(m2m_table), + our_pk=fk_to_us["other_column"], + our_column=fk_to_us["column"], + other_column=escape_sqlite(other_column), + value=value, + ) ) - ) # _search support: fts_table = info[name]["tables"].get(table, {}).get("fts_table") From af4ce463e7518f9d7828b846efd5b528a1905eca Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 30 Jul 2018 21:00:51 -0700 Subject: [PATCH 3/3] Hacky way of adding to human description --- datasette/views/table.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/datasette/views/table.py b/datasette/views/table.py index 6562bdaa..0f18a429 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -305,6 +305,7 @@ class TableView(RowTableShared): where_clauses, params = filters.build_where_clauses() # Hacky thing for ?_m2m_ad_targets__target_id=9a8c6 + extra_human_descriptions = [] for m2m_key in request.args: if m2m_key.startswith("_m2m_"): rest = m2m_key.split("_m2m_", 1)[1] @@ -328,6 +329,9 @@ class TableView(RowTableShared): value=value, ) ) + extra_human_descriptions.append( + '{} contains "{}"'.format(m2m_table, value) + ) # _search support: fts_table = info[name]["tables"].get(table, {}).get("fts_table") @@ -744,6 +748,9 @@ class TableView(RowTableShared): [b for b in [human_description_en, sorted_by] if b] ) + if extra_human_descriptions: + human_description_en += " and ".join(extra_human_descriptions) + async def extra_template(): display_columns, display_rows = await self.display_columns_and_rows( name,