Skip to content

Commit 1ab3165

Browse files
committed
Attempt at fixing #158 by returning raw values from the DB
`return_model=True` would break any rows with JSONField as it would try to convert from a JSON string to a Python object. However, the original input data was already partially converted. By forcing the database to return the values it inserted, we get a raw JSON string and the converted applies normally.
1 parent 9a79983 commit 1ab3165

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

psqlextra/query.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,14 @@ def bulk_insert(
165165
deduped_rows.append(row)
166166

167167
compiler = self._build_insert_compiler(deduped_rows, using=using)
168-
objs = compiler.execute_sql(return_id=True)
168+
objs = compiler.execute_sql(return_id=not return_model)
169169
if return_model:
170170
return [
171171
self._create_model_instance(dict(row, **obj), compiler.using)
172172
for row, obj in zip(deduped_rows, objs)
173173
]
174-
else:
175-
return [dict(row, **obj) for row, obj in zip(deduped_rows, objs)]
174+
175+
return [dict(row, **obj) for row, obj in zip(deduped_rows, objs)]
176176

177177
def insert(self, using: Optional[str] = None, **fields):
178178
"""Creates a new record in the database.

tests/test_on_conflict.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import django
12
import pytest
23

34
from django.core.exceptions import SuspiciousOperation
45
from django.db import connection, models
6+
from django.utils import timezone
57

68
from psqlextra.fields import HStoreField
79
from psqlextra.models import PostgresModel
@@ -412,3 +414,39 @@ def test_bulk_return_models(conflict_action):
412414
for index, obj in enumerate(objs, 1):
413415
assert isinstance(obj, model)
414416
assert obj.id == index
417+
418+
419+
@pytest.mark.skipif(
420+
django.VERSION < (3, 1),
421+
reason="Django < 3.1 doesn't implement JSONField",
422+
)
423+
@pytest.mark.parametrize("conflict_action", ConflictAction.all())
424+
def test_bulk_return_models_converters(conflict_action):
425+
"""Tests whether converters are properly applied when using
426+
return_model=True."""
427+
428+
model = get_fake_model(
429+
{
430+
"name": models.TextField(unique=True),
431+
"data": models.JSONField(unique=True),
432+
"updated_at": models.DateTimeField(),
433+
}
434+
)
435+
436+
now = timezone.now()
437+
438+
rows = [
439+
dict(name="John Smith", data={"a": 1}, updated_at=now.isoformat()),
440+
dict(name="Jane Doe", data={"b": 2}, updated_at=now),
441+
]
442+
443+
objs = model.objects.on_conflict(["name"], conflict_action).bulk_insert(
444+
rows, return_model=True
445+
)
446+
447+
for index, (obj, row) in enumerate(zip(objs, rows), 1):
448+
assert isinstance(obj, model)
449+
assert obj.id == index
450+
assert obj.name == row["name"]
451+
assert obj.data == row["data"]
452+
assert obj.updated_at == now

0 commit comments

Comments
 (0)