Skip to content

Commit

Permalink
Attempt at fixing #158 by returning raw values from the DB
Browse files Browse the repository at this point in the history
`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.
  • Loading branch information
Photonios committed Nov 23, 2021
1 parent 9a79983 commit 1ab3165
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
6 changes: 3 additions & 3 deletions psqlextra/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,14 @@ def bulk_insert(
deduped_rows.append(row)

compiler = self._build_insert_compiler(deduped_rows, using=using)
objs = compiler.execute_sql(return_id=True)
objs = compiler.execute_sql(return_id=not return_model)
if return_model:
return [
self._create_model_instance(dict(row, **obj), compiler.using)
for row, obj in zip(deduped_rows, objs)
]
else:
return [dict(row, **obj) for row, obj in zip(deduped_rows, objs)]

return [dict(row, **obj) for row, obj in zip(deduped_rows, objs)]

def insert(self, using: Optional[str] = None, **fields):
"""Creates a new record in the database.
Expand Down
38 changes: 38 additions & 0 deletions tests/test_on_conflict.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import django
import pytest

from django.core.exceptions import SuspiciousOperation
from django.db import connection, models
from django.utils import timezone

from psqlextra.fields import HStoreField
from psqlextra.models import PostgresModel
Expand Down Expand Up @@ -412,3 +414,39 @@ def test_bulk_return_models(conflict_action):
for index, obj in enumerate(objs, 1):
assert isinstance(obj, model)
assert obj.id == index


@pytest.mark.skipif(
django.VERSION < (3, 1),
reason="Django < 3.1 doesn't implement JSONField",
)
@pytest.mark.parametrize("conflict_action", ConflictAction.all())
def test_bulk_return_models_converters(conflict_action):
"""Tests whether converters are properly applied when using
return_model=True."""

model = get_fake_model(
{
"name": models.TextField(unique=True),
"data": models.JSONField(unique=True),
"updated_at": models.DateTimeField(),
}
)

now = timezone.now()

rows = [
dict(name="John Smith", data={"a": 1}, updated_at=now.isoformat()),
dict(name="Jane Doe", data={"b": 2}, updated_at=now),
]

objs = model.objects.on_conflict(["name"], conflict_action).bulk_insert(
rows, return_model=True
)

for index, (obj, row) in enumerate(zip(objs, rows), 1):
assert isinstance(obj, model)
assert obj.id == index
assert obj.name == row["name"]
assert obj.data == row["data"]
assert obj.updated_at == now

0 comments on commit 1ab3165

Please sign in to comment.