mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-07-24 01:44:31 +02:00
Improved foreign_keys= argument, closes #17
This commit is contained in:
parent
f8d3b7cfe5
commit
557dc3f9a7
3 changed files with 57 additions and 6 deletions
|
|
@ -125,6 +125,43 @@ After inserting a row like this, the ``dogs.last_rowid`` property will return th
|
|||
|
||||
The ``dogs.last_pk`` property will return the last inserted primary key value, if you specified one. This can be very useful when writing code that creates foreign key or many-to-many relationships.
|
||||
|
||||
Explicitly creating a table
|
||||
---------------------------
|
||||
|
||||
You can directly create a new table without inserting any data into it using the ``.create()`` method::
|
||||
|
||||
db["cats"].create({
|
||||
"id": int,
|
||||
"name": str,
|
||||
"weight": float,
|
||||
}, pk="id")
|
||||
|
||||
The first argument here is a dictionary specifying the columns you would like to create. Each column is paired with a Python type indicating the type of column. See :ref:`python_api_add_column` for full details on how these types work.
|
||||
|
||||
This method takes optional arguments ``pk=``, ``column_order=`` and ``foreign_keys=``.
|
||||
|
||||
Specifying foreign keys
|
||||
-----------------------
|
||||
|
||||
Any operation that can create a table (``.create()``, ``.insert()``, ``.insert_all()``, ``.upsert()`` and ``.upsert_all()``) accepts an optional ``foreign_keys=`` argument which can be used to set up foreign key constraints for the table that is being created.
|
||||
|
||||
If you are using your database with `Datasette <https://datasette.readthedocs.io/>`__, Datasette will detect these constraints and use them to generate hyperlinks to associated records.
|
||||
|
||||
The ``foreign_keys`` argument takes a sequence of three-tuples, each one specifying the column, other table and other column that should be used to create the relationship. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
db["authors"].insert_all([
|
||||
{"id": 1, "name": "Sally"},
|
||||
{"id": 2, "name": "Asheesh"}
|
||||
], pk="id")
|
||||
db["books"].insert_all([
|
||||
{"title": "Hedgehogs of the world", "author_id": 1},
|
||||
{"title": "How to train your wolf", "author_id": 2},
|
||||
], foreign_keys=[
|
||||
("author_id", "authors", "id")
|
||||
])
|
||||
|
||||
Bulk inserts
|
||||
============
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,14 @@ class Database:
|
|||
if hash_id:
|
||||
column_items.insert(0, (hash_id, str))
|
||||
pk = hash_id
|
||||
# Sanity check foreign_keys point to existing tables
|
||||
for _, fk_other_table, fk_other_column in foreign_keys:
|
||||
if not any(
|
||||
c for c in self[fk_other_table].columns if c.name == fk_other_column
|
||||
):
|
||||
raise AlterError(
|
||||
"No such column: {}.{}".format(fk_other_table, fk_other_column)
|
||||
)
|
||||
extra = ""
|
||||
columns_sql = ",\n".join(
|
||||
" [{col_name}] {col_type}{primary_key}{references}".format(
|
||||
|
|
@ -117,8 +125,8 @@ class Database:
|
|||
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],
|
||||
other_table=foreign_keys_by_name[col_name][1],
|
||||
other_column=foreign_keys_by_name[col_name][2],
|
||||
)
|
||||
if col_name in foreign_keys_by_name
|
||||
else ""
|
||||
|
|
|
|||
|
|
@ -96,10 +96,7 @@ def test_create_table_works_for_m2m_with_only_foreign_keys(fresh_db):
|
|||
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"),
|
||||
),
|
||||
foreign_keys=(("one_id", "one", "id"), ("two_id", "two", "id")),
|
||||
)
|
||||
assert [
|
||||
{"name": "one_id", "type": "INTEGER"},
|
||||
|
|
@ -124,6 +121,15 @@ def test_create_table_works_for_m2m_with_only_foreign_keys(fresh_db):
|
|||
)
|
||||
|
||||
|
||||
def test_create_error_if_invalid_foreign_keys(fresh_db):
|
||||
with pytest.raises(AlterError):
|
||||
fresh_db["one"].insert(
|
||||
{"id": 1, "ref_id": 3},
|
||||
pk="id",
|
||||
foreign_keys=(("ref_id", "bad_table", "bad_column"),),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"col_name,col_type,expected_schema",
|
||||
(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue