Skip to content

Commit 54a2269

Browse files
committed
Optional second argument to .lookup() to populate extra columns, closes #339
1 parent 9cda5b0 commit 54a2269

File tree

3 files changed

+53
-11
lines changed

3 files changed

+53
-11
lines changed

docs/python-api.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,16 @@ If you pass in a dictionary with multiple values, both values will be used to in
824824
})
825825
})
826826
827+
The ``.lookup()`` method has an optional second argument which can be used to populate other columns in the table but only if the row does not exist yet. These columns will not be included in the unique index.
828+
829+
To create a species record with a note on when it was first seen, you can use this:
830+
831+
.. code-block:: python
832+
833+
db["Species"].lookup({"name": "Palm"}, {"first_seen": "2021-03-04"})
834+
835+
The first time this is called the record will be created for ``name="Palm"``. Any subsequent calls with that name will ignore the second argument, even if it includes different values.
836+
827837
.. _python_api_extracts:
828838

829839
Populating lookup tables automatically during insert/upsert

sqlite_utils/db.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,7 +2713,11 @@ def add_missing_columns(self, records: Iterable[Dict[str, Any]]) -> "Table":
27132713
self.add_column(col_name, col_type)
27142714
return self
27152715

2716-
def lookup(self, column_values: Dict[str, Any]):
2716+
def lookup(
2717+
self,
2718+
lookup_values: Dict[str, Any],
2719+
extra_values: Optional[Dict[str, Any]] = None,
2720+
):
27172721
"""
27182722
Create or populate a lookup table with the specified values.
27192723
@@ -2725,28 +2729,36 @@ def lookup(self, column_values: Dict[str, Any]):
27252729
It will then insert a new row with the ``name`` set to ``Palm`` and return the
27262730
new integer primary key value.
27272731
2732+
An optional second argument can be provided with more ``name: value`` pairs to
2733+
be included only if the record is being created for the first time. These will
2734+
be ignored on subsequent lookup calls for records that already exist.
2735+
27282736
See :ref:`python_api_lookup_tables` for more details.
27292737
"""
2730-
# lookups is a dictionary - all columns will be used for a unique index
2731-
assert isinstance(column_values, dict)
2738+
assert isinstance(lookup_values, dict)
2739+
if extra_values is not None:
2740+
assert isinstance(extra_values, dict)
2741+
combined_values = dict(lookup_values)
2742+
if extra_values is not None:
2743+
combined_values.update(extra_values)
27322744
if self.exists():
2733-
self.add_missing_columns([column_values])
2745+
self.add_missing_columns([combined_values])
27342746
unique_column_sets = [set(i.columns) for i in self.indexes]
2735-
if set(column_values.keys()) not in unique_column_sets:
2736-
self.create_index(column_values.keys(), unique=True)
2737-
wheres = ["[{}] = ?".format(column) for column in column_values]
2747+
if set(lookup_values.keys()) not in unique_column_sets:
2748+
self.create_index(lookup_values.keys(), unique=True)
2749+
wheres = ["[{}] = ?".format(column) for column in lookup_values]
27382750
rows = list(
27392751
self.rows_where(
2740-
" and ".join(wheres), [value for _, value in column_values.items()]
2752+
" and ".join(wheres), [value for _, value in lookup_values.items()]
27412753
)
27422754
)
27432755
try:
27442756
return rows[0]["id"]
27452757
except IndexError:
2746-
return self.insert(column_values, pk="id").last_pk
2758+
return self.insert(combined_values, pk="id").last_pk
27472759
else:
2748-
pk = self.insert(column_values, pk="id").last_pk
2749-
self.create_index(column_values.keys(), unique=True)
2760+
pk = self.insert(combined_values, pk="id").last_pk
2761+
self.create_index(lookup_values.keys(), unique=True)
27502762
return pk
27512763

27522764
def m2m(

tests/test_lookup.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,23 @@ def test_lookup_fails_if_constraint_cannot_be_added(fresh_db):
6666
# This will fail because the name column is not unique
6767
with pytest.raises(Exception, match="UNIQUE constraint failed"):
6868
species.lookup({"name": "Palm"})
69+
70+
71+
def test_lookup_with_extra_values(fresh_db):
72+
species = fresh_db["species"]
73+
id = species.lookup({"name": "Palm", "type": "Tree"}, {"first_seen": "2020-01-01"})
74+
assert species.get(id) == {
75+
"id": 1,
76+
"name": "Palm",
77+
"type": "Tree",
78+
"first_seen": "2020-01-01",
79+
}
80+
# A subsequent lookup() should ignore the second dictionary
81+
id2 = species.lookup({"name": "Palm", "type": "Tree"}, {"first_seen": "2021-02-02"})
82+
assert id2 == id
83+
assert species.get(id2) == {
84+
"id": 1,
85+
"name": "Palm",
86+
"type": "Tree",
87+
"first_seen": "2020-01-01",
88+
}

0 commit comments

Comments
 (0)