Skip to content

Commit 65b4688

Browse files
committed
Stop updating on conflict if update_condition is False but not None
Some users of this library set `update_condition=0` on `upsert` for not updating anything on conflict. The `upsert` documentation says: > update_condition: > Only update if this SQL expression evaluates to true. A value evaluating to Python `False` is ignored while the documentation says no update will be done. [#186513018]
1 parent e5503cb commit 65b4688

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

psqlextra/query.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,9 @@ def upsert(
327327

328328
self.on_conflict(
329329
conflict_target,
330-
ConflictAction.UPDATE,
330+
ConflictAction.UPDATE
331+
if (update_condition or update_condition is None)
332+
else ConflictAction.NOTHING,
331333
index_predicate=index_predicate,
332334
update_condition=update_condition,
333335
update_values=update_values,

tests/test_upsert.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.db import connection, models
55
from django.db.models import F, Q
66
from django.db.models.expressions import CombinedExpression, Value
7+
from django.test.utils import CaptureQueriesContext
78

89
from psqlextra.expressions import ExcludedCol
910
from psqlextra.fields import HStoreField
@@ -144,6 +145,35 @@ def test_upsert_with_update_condition():
144145
assert obj1.active
145146

146147

148+
@pytest.mark.parametrize("update_condition_value", [0, False])
149+
def test_upsert_with_update_condition_false(update_condition_value):
150+
"""Tests that an expression can be used as an upsert update condition."""
151+
152+
model = get_fake_model(
153+
{
154+
"name": models.TextField(unique=True),
155+
"priority": models.IntegerField(),
156+
"active": models.BooleanField(),
157+
}
158+
)
159+
160+
obj1 = model.objects.create(name="joe", priority=1, active=False)
161+
162+
with CaptureQueriesContext(connection) as ctx:
163+
upsert_result = model.objects.upsert(
164+
conflict_target=["name"],
165+
update_condition=update_condition_value,
166+
fields=dict(name="joe", priority=2, active=True),
167+
)
168+
assert upsert_result is None
169+
assert len(ctx) == 1
170+
assert 'ON CONFLICT ("name") DO NOTHING' in ctx[0]["sql"]
171+
172+
obj1.refresh_from_db()
173+
assert obj1.priority == 1
174+
assert not obj1.active
175+
176+
147177
def test_upsert_with_update_values():
148178
"""Tests that the default update values can be overriden with custom
149179
expressions."""

0 commit comments

Comments
 (0)