Skip to content

Commit

Permalink
Fix bulk_update() on I18n_JSON fields for heterogenous values
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls committed Feb 20, 2024
1 parent 10fdc68 commit 071895c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
14 changes: 14 additions & 0 deletions arches/app/models/fields/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from arches.app.utils import import_class_from_string
from django.utils.translation import gettext_lazy as _
from django.db.models import JSONField
from django.db.models.functions.comparison import Cast
from django.db.models.sql.compiler import SQLInsertCompiler
from django.db.models.sql.where import NothingNode
from django.utils.translation import get_language
Expand Down Expand Up @@ -235,6 +236,17 @@ def __init__(self, value=None, lang=None, use_nulls=False, attname=None):
def _parse(self, value, lang, use_nulls):
ret = {}

if isinstance(value, Cast):
# Django 4.2 regression: bulk_update() sends Cast expressions
# https://code.djangoproject.com/ticket/35167
values = set(case.result.value for case in value.source_expressions[0].cases)
value = list(values)[0]
if len(values) > 1:
# Prevent silent data loss.
raise NotImplementedError(
"Heterogenous values provided to I18n_JSON field bulk_update():\n"
f"{tuple(str(v) for v in values)}"
)
if isinstance(value, str):
try:
ret = json.loads(value)
Expand All @@ -246,6 +258,8 @@ def _parse(self, value, lang, use_nulls):
ret = value.raw_value
elif isinstance(value, dict):
ret = value
else:
raise TypeError(value)
self.raw_value = ret

if "i18n_properties" in self.raw_value:
Expand Down
47 changes: 46 additions & 1 deletion tests/localization/field_tests.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import json
import unittest

from arches.app.models.models import DDataType
from arches.app.models.fields.i18n import I18n_String, I18n_TextField, I18n_JSON, I18n_JSONField
from tests.base_test import ArchesTestCase
from django.contrib.gis.db import models
from django.utils import translation
from django.db import connection

# these tests can be run from the command line via
# python manage.py test tests/localization/field_tests.py --pattern="*.py" --settings="tests.test_settings"
# python manage.py test tests.localization.field_tests --settings="tests.test_settings"


class Customi18nTextFieldTests(ArchesTestCase):
Expand Down Expand Up @@ -355,3 +358,45 @@ def test_i18nJSONField_can_handle_different_initial_states(self):
m.save()
m = self.LocalizationTestJsonModel.objects.get(pk=3)
self.assertEqual(m.config.raw_value, expected_output_json)


class I18nJSONFieldBulkUpdateTests(ArchesTestCase):
def test_bulk_update_node_config_homogenous_value(self):
new_config = I18n_JSON({
"en": "some",
"zh": "json",
})
for_bulk_update = []
for dt in DDataType.objects.all()[:3]:
dt.defaultconfig = new_config
for_bulk_update.append(dt)

DDataType.objects.bulk_update(for_bulk_update, fields=["defaultconfig"])

for i, obj in enumerate(for_bulk_update):
with self.subTest(obj_index=i):
obj.refresh_from_db()
self.assertEqual(str(obj.defaultconfig), str(new_config))

@unittest.skip("https://github.com/archesproject/arches/issues/10619")
def test_bulk_update_heterogenous_values(self):
new_configs = [
I18n_JSON({
"en": "some",
"zh": "json",
}),
I18n_JSON({}),
None,
]
for_bulk_update = []
for i, dt in enumerate(DDataType.objects.all()[:3]):
dt.defaultconfig = new_configs[i]
for_bulk_update.append(dt)

DDataType.objects.bulk_update(for_bulk_update, fields=["defaultconfig"])

for i, obj in enumerate(for_bulk_update):
new_config_as_string = str(new_configs[i])
with self.subTest(new_config=new_config_as_string):
obj.refresh_from_db()
self.assertEqual(str(obj.defaultconfig), new_config_as_string)

0 comments on commit 071895c

Please sign in to comment.