diff --git a/docs/cli.rst b/docs/cli.rst index cd5b6fd..6aa2833 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -348,6 +348,57 @@ This will create a table called ``mytable`` with two columns - an integer ``id`` You can pass as many column-name column-type pairs as you like. Valid types are ``integer``, ``text``, ``float`` and ``blob``. +You can specify columns that should be NOT NULL using ``--not-null colname``. You can specify default values for columns using ``--default colname defaultvalue``. + +:: + + $ sqlite-utils create-table mydb.db mytable \ + id integer \ + name text \ + age integer \ + is_good integer \ + --not-null name \ + --not-null age \ + --default is_good 1 \ + --pk=id + + $ sqlite-utils tables mydb.db --schema -t + table schema + ------- -------------------------------- + mytable CREATE TABLE [mytable] ( + [id] INTEGER PRIMARY KEY, + [name] TEXT NOT NULL, + [age] INTEGER NOT NULL, + [is_good] INTEGER DEFAULT '1' + ) + +You can specify foreign key relationships between the tables you are creating using ``--fk colname othertable othercolumn``:: + + $ sqlite-utils create-table books.db authors \ + id integer \ + name text \ + --pk=id + + $ sqlite-utils create-table books.db books \ + id integer \ + title text \ + author_id integer \ + --pk=id \ + --fk author_id authors id + + $ sqlite-utils tables books.db --schema -t + table schema + ------- ------------------------------------------------- + authors CREATE TABLE [authors] ( + [id] INTEGER PRIMARY KEY, + [name] TEXT + ) + books CREATE TABLE [books] ( + [id] INTEGER PRIMARY KEY, + [title] TEXT, + [author_id] INTEGER REFERENCES [authors]([id]) + ) + .. _cli_add_column: Adding columns diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index a6ad7ca..a29c3af 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -539,7 +539,22 @@ def upsert( @click.argument("table") @click.argument("columns", nargs=-1, required=True) @click.option("--pk", help="Column to use as primary key") -def create_table(path, table, columns, pk): +@click.option( + "--not-null", multiple=True, help="Columns that should be created as NOT NULL", +) +@click.option( + "--default", + multiple=True, + type=(str, str), + help="Default value that should be set for a column", +) +@click.option( + "--fk", + multiple=True, + type=(str, str, str), + help="Column, other table, other column to set as a foreign key", +) +def create_table(path, table, columns, pk, not_null, default, fk): "Add an index to the specified table covering the specified columns" db = sqlite_utils.Database(path) if len(columns) % 2 == 1: @@ -556,7 +571,9 @@ def create_table(path, table, columns, pk): "column types must be one of {}".format(VALID_COLUMN_TYPES) ) coltypes[name] = ctype.upper() - db[table].create(coltypes, pk=pk) + db[table].create( + coltypes, pk=pk, not_null=not_null, defaults=dict(default), foreign_keys=fk + ) @cli.command() diff --git a/tests/test_cli.py b/tests/test_cli.py index d002ac2..36e9b72 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -863,15 +863,17 @@ def test_upsert_alter(db_path, tmpdir): ) -def test_create_table(): - runner = CliRunner() - with runner.isolated_filesystem(): - result = runner.invoke( - cli.cli, +@pytest.mark.parametrize( + "args,schema", + [ + # No primary key + ( + ["name", "text", "age", "integer",], + ("CREATE TABLE [t] (\n [name] TEXT,\n [age] INTEGER\n)"), + ), + # All types: + ( [ - "create-table", - "test.db", - "t", "id", "integer", "name", @@ -885,15 +887,76 @@ def test_create_table(): "--pk", "id", ], + ( + "CREATE TABLE [t] (\n" + " [id] INTEGER PRIMARY KEY,\n" + " [name] TEXT,\n" + " [age] INTEGER,\n" + " [weight] FLOAT,\n" + " [thumbnail] BLOB\n" + ")" + ), + ), + # Not null: + ( + ["name", "text", "--not-null", "name"], + ("CREATE TABLE [t] (\n" " [name] TEXT NOT NULL\n" ")"), + ), + # Default: + ( + ["age", "integer", "--default", "age", "3"], + ("CREATE TABLE [t] (\n" " [age] INTEGER DEFAULT '3'\n" ")"), + ), + ], +) +def test_create_table(args, schema): + runner = CliRunner() + with runner.isolated_filesystem(): + result = runner.invoke( + cli.cli, ["create-table", "test.db", "t",] + args, catch_exceptions=False ) assert 0 == result.exit_code db = Database("test.db") + assert schema == db["t"].schema + + +def test_create_table_foreign_key(): + runner = CliRunner() + creates = ( + ["authors", "id", "integer", "name", "text", "--pk", "id"], + [ + "books", + "id", + "integer", + "title", + "text", + "author_id", + "integer", + "--pk", + "id", + "--fk", + "author_id", + "authors", + "id", + ], + ) + with runner.isolated_filesystem(): + for args in creates: + result = runner.invoke( + cli.cli, ["create-table", "books.db"] + args, catch_exceptions=False + ) + assert 0 == result.exit_code + db = Database("books.db") assert ( - "CREATE TABLE [t] (\n" + "CREATE TABLE [authors] (\n" " [id] INTEGER PRIMARY KEY,\n" - " [name] TEXT,\n" - " [age] INTEGER,\n" - " [weight] FLOAT,\n" - " [thumbnail] BLOB\n" + " [name] TEXT\n" ")" - ) == db["t"].schema + ) == db["authors"].schema + assert ( + "CREATE TABLE [books] (\n" + " [id] INTEGER PRIMARY KEY,\n" + " [title] TEXT,\n" + " [author_id] INTEGER REFERENCES [authors]([id])\n" + ")" + ) == db["books"].schema