sqlite-utils insert --code option, closes #684

This commit is contained in:
Simon Willison 2026-07-07 18:48:57 -07:00
commit 569608e40f
5 changed files with 347 additions and 75 deletions

View file

@ -10,6 +10,7 @@ Unreleased
----------
- ``sqlite-utils query`` can now read the SQL query from standard input by passing ``-`` in place of the query, for example ``echo "select * from dogs" | sqlite-utils query dogs.db -``. (:issue:`765`)
- ``sqlite-utils insert`` and ``sqlite-utils upsert`` now accept a ``--code`` option for providing a block of Python code (or a path to a ``.py`` file) that defines a ``rows()`` function or ``rows`` iterable of rows to insert, as an alternative to importing from a file. (:issue:`684`)
.. _v4_0:

View file

@ -230,7 +230,7 @@ See :ref:`cli_inserting_data`, :ref:`cli_insert_csv_tsv`, :ref:`cli_insert_unstr
::
Usage: sqlite-utils insert [OPTIONS] PATH TABLE FILE
Usage: sqlite-utils insert [OPTIONS] PATH TABLE [FILE]
Insert records from FILE into a table, creating the table if it does not
already exist.
@ -272,8 +272,20 @@ See :ref:`cli_inserting_data`, :ref:`cli_insert_csv_tsv`, :ref:`cli_insert_unstr
echo 'A bunch of words' | sqlite-utils insert words.db words - \
--text --convert '({"word": w} for w in text.split())'
Instead of a FILE you can use --code to provide a block of Python code that
defines the rows to insert, as either a rows() function that yields
dictionaries or a "rows" iterable. --code can also be a path to a .py file:
sqlite-utils insert data.db creatures --code '
def rows():
yield {"id": 1, "name": "Cleo"}
yield {"id": 2, "name": "Suna"}
' --pk id
Options:
--pk TEXT Columns to use as the primary key, e.g. id
--code TEXT Python code defining a rows() function or iterable
of rows to insert
--flatten Flatten nested JSON objects, so {"a": {"b": 1}}
becomes {"a_b": 1}
--nl Expect newline-delimited JSON
@ -315,7 +327,7 @@ See :ref:`cli_upsert`.
::
Usage: sqlite-utils upsert [OPTIONS] PATH TABLE FILE
Usage: sqlite-utils upsert [OPTIONS] PATH TABLE [FILE]
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
@ -332,6 +344,8 @@ See :ref:`cli_upsert`.
Options:
--pk TEXT Columns to use as the primary key, e.g. id
--code TEXT Python code defining a rows() function or iterable
of rows to insert
--flatten Flatten nested JSON objects, so {"a": {"b": 1}}
becomes {"a_b": 1}
--nl Expect newline-delimited JSON

View file

@ -1561,6 +1561,27 @@ The result looks like this:
COMMIT;
.. _cli_insert_code:
Inserting rows generated by Python code
=======================================
Instead of providing a ``FILE`` to import, you can use the ``--code`` option to pass a block of Python code that generates the rows to insert. This is the command-line equivalent of calling ``db["creatures"].insert_all(rows())`` from the :ref:`Python API <python_api>`.
Your code should define either a ``rows()`` function that returns or yields dictionaries, or a ``rows`` iterable such as a list of dictionaries:
.. code-block:: bash
sqlite-utils insert data.db creatures --code '
def rows():
yield {"id": 1, "name": "Cleo"}
yield {"id": 2, "name": "Suna"}
' --pk id
``--code`` can also be given a path to a Python ``.py`` file.
The ``--code`` option works with both ``sqlite-utils insert`` and ``sqlite-utils upsert``, and composes with table options such as ``--pk``, ``--replace``, ``--alter``, ``--not-null`` and ``--default``. It cannot be combined with a ``FILE`` argument or with input format options such as ``--csv`` or ``--convert``.
.. _cli_insert_replace:
Insert-replacing data