From 98964b05a4c76b5bb343c4d824e8a975c47d2b2e Mon Sep 17 00:00:00 2001 From: sinisaos Date: Thu, 8 Jan 2026 18:36:14 +0100 Subject: [PATCH] target is required on do_update --- .../src/piccolo/query_clauses/on_conflict.rst | 6 ++++-- piccolo/query/mixins.py | 5 +++++ tests/table/test_insert.py | 21 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/src/piccolo/query_clauses/on_conflict.rst b/docs/src/piccolo/query_clauses/on_conflict.rst index 9ae8444e3..c3adfa51c 100644 --- a/docs/src/piccolo/query_clauses/on_conflict.rst +++ b/docs/src/piccolo/query_clauses/on_conflict.rst @@ -78,6 +78,7 @@ Instead, if we want to update the ``popularity``: ... Band(name="Pythonistas", popularity=1200) ... ).on_conflict( ... action="DO UPDATE", + ... target=Band.name, ... values=[Band.popularity] ... ) @@ -93,8 +94,9 @@ If we fetch the data from the database, we'll see that it was updated: Using the ``target`` argument, we can specify which constraint we're concerned with. By specifying ``target=Band.name`` we're only concerned with the unique -constraint for the ``band`` column. If you omit the ``target`` argument, then -it works for all constraints on the table. +constraint for the ``band`` column. If you omit the ``target`` argument on +``DO NOTHING`` action, then it works for all constraints on the table. For +``DO UPDATE`` action, ``target`` is mandatory and must be provided. .. code-block:: python :emphasize-lines: 5 diff --git a/piccolo/query/mixins.py b/piccolo/query/mixins.py index 16b69e8b7..178d793bf 100644 --- a/piccolo/query/mixins.py +++ b/piccolo/query/mixins.py @@ -769,6 +769,11 @@ def on_conflict( else: raise ValueError("Unrecognised `on conflict` action.") + if target is None and action_ == OnConflictAction.do_update: + raise ValueError( + "The `target` option must be provided with DO UPDATE." + ) + if where and action_ == OnConflictAction.do_nothing: raise ValueError( "The `where` option can only be used with DO NOTHING." diff --git a/tests/table/test_insert.py b/tests/table/test_insert.py index bf298cd09..19c1b0acb 100644 --- a/tests/table/test_insert.py +++ b/tests/table/test_insert.py @@ -174,6 +174,27 @@ def test_do_update_tuple_values(self): ], ) + def test_do_update_no_target(self): + """ + Make sure that `DO UPDATE` with no `target` raises an exception. + """ + Band = self.Band + + new_popularity = self.band.popularity + 1000 + + with self.assertRaises(ValueError) as manager: + Band.insert( + Band(name=self.band.name, popularity=new_popularity) + ).on_conflict( + action="DO UPDATE", + values=[(Band.popularity, new_popularity + 2000)], + ).run_sync() + + self.assertEqual( + manager.exception.__str__(), + "The `target` option must be provided with DO UPDATE.", + ) + def test_do_update_no_values(self): """ Make sure that `DO UPDATE` with no `values` raises an exception.