create-table --not-null, --default, --fk, refs #27

This commit is contained in:
Simon Willison 2020-05-03 08:09:00 -07:00
commit 4d5916075d
3 changed files with 147 additions and 16 deletions

View file

@ -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

View file

@ -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()

View file

@ -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