diff --git a/docs/cli.rst b/docs/cli.rst index f9ef84c..8e47ad6 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -310,6 +310,19 @@ If you omit the other table and other column references ``sqlite-utils`` will at See :ref:`python_api_add_foreign_key` in the Python API documentation for further details, including how the automatic table guessing mechanism works. +.. _cli_defaults_not_null: + +Setting defaults and not null constraints +========================================= + +You can use the ``--not-null`` and ``--default`` options (to both ``insert`` and ``upsert``) to specify columns that should be ``NOT NULL`` or to set database defaults for one or more specific columns:: + + $ sqlite-utils insert dogs.db dogs_with_scores dogs-with-scores.json \ + --not-null=age \ + --not-null=name \ + --default age 2 \ + --default score 5 + .. _cli_create_index: Creating indexes diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index 0424eda..0df540a 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -299,6 +299,17 @@ def insert_upsert_options(fn): is_flag=True, help="Alter existing table to add any missing columns", ), + 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", + ), ) ): fn = decorator(fn) @@ -306,7 +317,18 @@ def insert_upsert_options(fn): def insert_upsert_implementation( - path, table, json_file, pk, nl, csv, batch_size, alter, upsert, ignore=False + path, + table, + json_file, + pk, + nl, + csv, + batch_size, + alter, + upsert, + ignore=False, + not_null=None, + default=None, ): db = sqlite_utils.Database(path) if nl and csv: @@ -328,6 +350,10 @@ def insert_upsert_implementation( else: method = db[table].insert_all extra_kwargs = {"ignore": ignore} + if not_null: + extra_kwargs["not_null"] = set(not_null) + if default: + extra_kwargs["defaults"] = dict(default) method(docs, pk=pk, batch_size=batch_size, alter=alter, **extra_kwargs) @@ -336,7 +362,9 @@ def insert_upsert_implementation( @click.option( "--ignore", is_flag=True, default=False, help="Ignore records if pk already exists" ) -def insert(path, table, json_file, pk, nl, csv, batch_size, alter, ignore): +def insert( + path, table, json_file, pk, nl, csv, batch_size, alter, ignore, not_null, default +): """ Insert records from JSON file into a table, creating the table if it does not already exist. @@ -354,19 +382,31 @@ def insert(path, table, json_file, pk, nl, csv, batch_size, alter, ignore): alter=alter, upsert=False, ignore=ignore, + not_null=not_null, + default=default, ) @cli.command() @insert_upsert_options -def upsert(path, table, json_file, pk, nl, csv, batch_size, alter): +def upsert(path, table, json_file, pk, nl, csv, batch_size, alter, not_null, default): """ Upsert records based on their primary key. Works like 'insert' but if an incoming record has a primary key that matches an existing record the existing record will be replaced. """ insert_upsert_implementation( - path, table, json_file, pk, nl, csv, batch_size, alter=alter, upsert=True + path, + table, + json_file, + pk, + nl, + csv, + batch_size, + alter=alter, + upsert=True, + not_null=not_null, + default=default, ) diff --git a/tests/test_cli.py b/tests/test_cli.py index 24bbc38..0316a51 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -417,6 +417,30 @@ def test_insert_multiple_with_primary_key(db_path, tmpdir): assert ["id"] == db["dogs"].pks +def test_insert_not_null_default(db_path, tmpdir): + json_path = str(tmpdir / "dogs.json") + dogs = [ + {"id": i, "name": "Cleo {}".format(i), "age": i + 3, "score": 10} + for i in range(1, 21) + ] + open(json_path, "w").write(json.dumps(dogs)) + result = CliRunner().invoke( + cli.cli, + ["insert", db_path, "dogs", json_path, "--pk", "id"] + + ["--not-null", "name", "--not-null", "age"] + + ["--default", "score", "5", "--default", "age", "1"], + ) + assert 0 == result.exit_code + db = Database(db_path) + assert ( + "CREATE TABLE [dogs] (\n" + " [id] INTEGER PRIMARY KEY,\n" + " [name] TEXT NOT NULL,\n" + " [age] INTEGER NOT NULL DEFAULT '1',\n" + " [score] INTEGER DEFAULT '5'\n)" + ) == db["dogs"].schema + + def test_insert_newline_delimited(db_path): result = CliRunner().invoke( cli.cli,