Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion datasette/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,8 +899,11 @@ async def expand_foreign_keys(self, database, table, column, values):
fk = [
foreign_key
for foreign_key in foreign_keys
if foreign_key["column"] == column
if foreign_key["columns"][0] == column
and len(foreign_key["columns"]) == 1
][0]
fk["column"] = fk["columns"][0]
fk["other_column"] = fk["other_columns"][0]
except IndexError:
return {}
label_column = await db.label_column_for_table(fk["other_table"])
Expand Down
6 changes: 5 additions & 1 deletion datasette/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ async def inner():
outgoing_foreign_keys = await db.foreign_keys_for_table(through_table)
try:
fk_to_us = [
fk for fk in outgoing_foreign_keys if fk["other_table"] == table
fk
for fk in outgoing_foreign_keys
if fk["other_table"] == table and len(fk["other_columns"]) == 1
][0]
fk_to_us["column"] = fk_to_us["columns"][0]
fk_to_us["other_column"] = fk_to_us["other_columns"][0]
except IndexError:
raise DatasetteError(
"Invalid _through - could not find corresponding foreign key"
Expand Down
2 changes: 1 addition & 1 deletion datasette/templates/row.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ <h2>Links from other tables</h2>
<li>
<a href="{{ other.link }}">
{{ "{:,}".format(other.count) }} row{% if other.count == 1 %}{% else %}s{% endif %}</a>
from {{ other.other_column }} in {{ other.other_table }}
from {{ other.other_columns_reference }} in {{ other.other_table }}
</li>
{% endfor %}
</ul>
Expand Down
37 changes: 14 additions & 23 deletions datasette/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,30 +523,21 @@ def detect_primary_keys(conn, table):

def get_outbound_foreign_keys(conn, table):
infos = conn.execute(f"PRAGMA foreign_key_list([{table}])").fetchall()
fks = []
fks = {}
for info in infos:
if info is not None:
id, seq, table_name, from_, to_, on_update, on_delete, match = info
fks.append(
{
"column": from_,
if id in fks:
fk_info = fks[id]
fk_info["columns"] += (from_,)
fk_info["other_columns"] += (to_,)
else:
fks[id] = {
"other_table": table_name,
"other_column": to_,
"id": id,
"seq": seq,
"columns": (from_,),
"other_columns": (to_,),
}
)
# Filter out compound foreign keys by removing any where "id" is not unique
id_counts = Counter(fk["id"] for fk in fks)
return [
{
"column": fk["column"],
"other_table": fk["other_table"],
"other_column": fk["other_column"],
}
for fk in fks
if id_counts[fk["id"]] == 1
]
return list(fks.values())


def get_all_foreign_keys(conn):
Expand All @@ -560,17 +551,17 @@ def get_all_foreign_keys(conn):
fks = get_outbound_foreign_keys(conn, table)
for fk in fks:
table_name = fk["other_table"]
from_ = fk["column"]
to_ = fk["other_column"]
from_ = fk["columns"]
to_ = fk["other_columns"]
if table_name not in table_to_foreign_keys:
# Weird edge case where something refers to a table that does
# not actually exist
continue
table_to_foreign_keys[table_name]["incoming"].append(
{"other_table": table, "column": to_, "other_column": from_}
{"other_table": table, "columns": to_, "other_columns": from_}
)
table_to_foreign_keys[table]["outgoing"].append(
{"other_table": table_name, "column": from_, "other_column": to_}
{"other_table": table_name, "columns": from_, "other_columns": to_}
)

return table_to_foreign_keys
Expand Down
48 changes: 33 additions & 15 deletions datasette/views/row.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ async def template_data():
)

async def foreign_key_tables(self, database, table, pk_values):
if len(pk_values) != 1:
return []
db = self.ds.databases[database]
all_foreign_keys = await db.get_all_foreign_keys()
foreign_keys = all_foreign_keys[table]["incoming"]
Expand All @@ -107,39 +105,59 @@ async def foreign_key_tables(self, database, table, pk_values):

sql = "select " + ", ".join(
[
"(select count(*) from {table} where {column}=:id)".format(
"(select count(*) from {table} where {condition})".format(
table=escape_sqlite(fk["other_table"]),
column=escape_sqlite(fk["other_column"]),
condition=" and ".join(
"{column}=:id{i}".format(column=escape_sqlite(column), i=i)
for i, column in enumerate(fk["other_columns"])
),
)
for fk in foreign_keys
]
)
try:
rows = list(await db.execute(sql, {"id": pk_values[0]}))
rows = list(
await db.execute(
sql, {"id{i}".format(i=i): pk for i, pk in enumerate(pk_values)}
)
)
except QueryInterrupted:
# Almost certainly hit the timeout
return []

foreign_table_counts = dict(
zip(
[(fk["other_table"], fk["other_column"]) for fk in foreign_keys],
[(fk["other_table"], fk["other_columns"]) for fk in foreign_keys],
list(rows[0]),
)
)
foreign_key_tables = []
for fk in foreign_keys:
count = (
foreign_table_counts.get((fk["other_table"], fk["other_column"])) or 0
foreign_table_counts.get((fk["other_table"], fk["other_columns"])) or 0
)
query_pairs = zip(fk["other_columns"], pk_values)
query = "&".join(
"{}={}".format(col + "__exact" if col.startswith("_") else col, pk)
for col, pk in query_pairs
)
link = "{}?{}".format(
self.ds.urls.table(database, fk["other_table"]), query
)
key = fk["other_column"]
if key.startswith("_"):
key += "__exact"
link = "{}?{}={}".format(
self.ds.urls.table(database, fk["other_table"]),
key,
",".join(pk_values),
if len(pk_values) == 1:
other_columns_reference = fk["other_columns"][0]
else:
other_columns_reference = "({})".format(", ".join(fk["other_columns"]))
foreign_key_tables.append(
{
**fk,
**{
"count": count,
"link": link,
"other_columns_reference": other_columns_reference,
},
}
)
foreign_key_tables.append({**fk, **{"count": count, "link": link}})
return foreign_key_tables


Expand Down
12 changes: 10 additions & 2 deletions datasette/views/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,15 @@ async def expandable_columns(self, database_name, table_name):
expandables = []
db = self.ds.databases[database_name]
for fk in await db.foreign_keys_for_table(table_name):
if len(fk["other_columns"]) > 1:
continue
label_column = await db.label_column_for_table(fk["other_table"])
expandables.append((fk, label_column))
singleton_fk = {
"other_table": fk["other_table"],
"other_column": fk["other_columns"][0],
"column": fk["columns"][0],
}
expandables.append((singleton_fk, label_column))
return expandables

async def post(self, request):
Expand Down Expand Up @@ -920,8 +927,9 @@ async def display_columns_and_rows(
)

column_to_foreign_key_table = {
fk["column"]: fk["other_table"]
fk["columns"][0]: fk["other_table"]
for fk in await db.foreign_keys_for_table(table_name)
if len(fk["columns"]) == 1
}

cell_rows = []
Expand Down
Loading