From acea54877ca65f08cef4f7260bd7f7fcabe31b90 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 28 Jul 2018 15:06:59 -0700 Subject: [PATCH] Create table now works for pure m2m where both rows are foreign keys --- sqlite_utils/db.py | 57 +++++++++++++++++++++++--------------------- tests/test_create.py | 16 +++++++++++++ 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index 1b441cf..5391143 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -25,26 +25,37 @@ class Database: ).fetchall() ] - def create_table(self, name, columns, pk=None): + def create_table(self, name, columns, pk=None, foreign_keys=None): + foreign_keys = foreign_keys or [] + foreign_keys_by_name = {fk[0]: fk for fk in foreign_keys} + extra = "" + columns = ",\n".join( + " {col_name} {col_type} {primary_key} {references}".format( + col_name=col_name, + col_type={ + float: "FLOAT", + int: "INTEGER", + bool: "INTEGER", + str: "TEXT", + None.__class__: "TEXT", + }[col_type], + primary_key=" PRIMARY KEY" if (pk == col_name) else "", + references=( + " REFERENCES [{other_table}({other_column})]".format( + other_table=foreign_keys_by_name[col_name][2], + other_column=foreign_keys_by_name[col_name][3], + ) + if col_name in foreign_keys_by_name + else "" + ), + ) + for col_name, col_type in columns.items() + ) sql = """CREATE TABLE {table} ( {columns} - ); + ){extra}; """.format( - table=name, - columns=",\n".join( - " {col_name} {col_type} {primary_key}".format( - col_name=col_name, - col_type={ - float: "FLOAT", - int: "INTEGER", - bool: "INTEGER", - str: "TEXT", - None.__class__: "TEXT", - }[col_type], - primary_key=" PRIMARY KEY" if (pk == col_name) else "", - ) - for col_name, col_type in columns.items() - ), + table=name, columns=columns, extra=extra ) self.conn.execute(sql) return self[name] @@ -66,16 +77,8 @@ class Table: return [Column(*row) for row in rows] def create(self, columns, pk=None, foreign_keys=None): - # Ignore columns in foreign_keys list - columns = { - name: value - for name, value in columns.items() - if name not in {fk[0] for fk in (foreign_keys or [])} - } - self.db.create_table(self.name, columns, pk=pk) - if foreign_keys: - for args in foreign_keys: - self.add_foreign_key(*args) + columns = {name: value for (name, value) in columns.items()} + self.db.create_table(self.name, columns, pk=pk, foreign_keys=foreign_keys) self.exists = True def drop(self): diff --git a/tests/test_create.py b/tests/test_create.py index 168d4e4..f7a8c77 100644 --- a/tests/test_create.py +++ b/tests/test_create.py @@ -38,3 +38,19 @@ def test_create_table_from_example(fresh_db, example, expected_columns): assert expected_columns == [ {"name": col.name, "type": col.type} for col in fresh_db["people"].columns ] + + +def test_create_table_works_for_m2m_with_only_foreign_keys(fresh_db): + fresh_db["one"].insert({"id": 1}, pk="id") + fresh_db["two"].insert({"id": 1}, pk="id") + fresh_db["m2m"].insert( + {"one_id": 1, "two_id": 1}, + foreign_keys=( + ("one_id", "INTEGER", "one", "id"), + ("two_id", "INTEGER", "two", "id"), + ), + ) + assert [ + {"name": "one_id", "type": "INTEGER"}, + {"name": "two_id", "type": "INTEGER"}, + ] == [{"name": col.name, "type": col.type} for col in fresh_db["m2m"].columns]