diff --git a/ckan/logic/converters.py b/ckan/logic/converters.py index a566a87410b..71cd05c95b9 100644 --- a/ckan/logic/converters.py +++ b/ckan/logic/converters.py @@ -26,14 +26,24 @@ def convert_to_extras(key, data, errors, context): def convert_from_extras(key, data, errors, context): - def remove_from_extras(data, key): - to_remove = [] - for data_key, data_value in six.iteritems(data): - if (data_key[0] == 'extras' - and data_key[1] == key): - to_remove.append(data_key) - for item in to_remove: - del data[item] + def remove_from_extras(data, idx): + for key in sorted(data): + if key[0] != 'extras' or key[1] < idx: + continue + if key[1] == idx: + del data[key] + + # Following block required for unflattening extras with + # "gaps" created sometimes by `convert_from_extra` + # validator : + # + # { + # ('extras', 0, 'key'): 'x', + # ('extras', 2, 'key): 'y' + # } + if key[1] > idx: + new_key = (key[0], key[1] - 1) + key[2:] + data[new_key] = data.pop(key) for data_key, data_value in six.iteritems(data): if (data_key[0] == 'extras' diff --git a/ckanext/example_idatasetform/plugin_v5.py b/ckanext/example_idatasetform/plugin_v5.py index 55cdca71265..fc31c90cba6 100644 --- a/ckanext/example_idatasetform/plugin_v5.py +++ b/ckanext/example_idatasetform/plugin_v5.py @@ -30,7 +30,9 @@ def show_package_schema(self): schema = super(ExampleIDatasetFormPlugin, self).show_package_schema() schema.update({ u'custom_text': [tk.get_converter(u'convert_from_extras'), - tk.get_validator(u'ignore_missing')] + tk.get_validator(u'ignore_missing')], + u'custom_text_2': [tk.get_converter(u'convert_from_extras'), + tk.get_validator(u'ignore_missing')], }) return schema diff --git a/ckanext/example_idatasetform/tests/test_example_idatasetform.py b/ckanext/example_idatasetform/tests/test_example_idatasetform.py index 42cec3bb57e..1e98dfea520 100644 --- a/ckanext/example_idatasetform/tests/test_example_idatasetform.py +++ b/ckanext/example_idatasetform/tests/test_example_idatasetform.py @@ -79,6 +79,40 @@ def test_custom_package_type_urls(self): url_for("fancy_type.edit", id="check") == "/fancy_type/edit/check" ) + def test_custom_field_with_extras(self): + dataset = factories.Dataset( + type='fancy_type', + name='test-dataset', + custom_text='custom-text', + extras=[ + {'key': 'key1', 'value': 'value1'}, + {'key': 'key2', 'value': 'value2'}, + ] + ) + assert dataset['custom_text'] == 'custom-text' + assert dataset['extras'] == [ + {'key': 'key1', 'value': 'value1'}, + {'key': 'key2', 'value': 'value2'}, + ] + + def test_mixed_extras(self): + dataset = factories.Dataset( + type='fancy_type', + name='test-dataset', + custom_text='custom-text', + extras=[ + {'key': 'key1', 'value': 'value1'}, + {'key': 'custom_text_2', 'value': 'custom-text-2'}, + {'key': 'key2', 'value': 'value2'}, + ], + ) + assert dataset['custom_text'] == 'custom-text' + assert dataset['custom_text_2'] == 'custom-text-2' + assert dataset['extras'] == [ + {'key': 'key1', 'value': 'value1'}, + {'key': 'key2', 'value': 'value2'}, + ] + @pytest.mark.ckan_config("ckan.plugins", u"example_idatasetform_v5") @pytest.mark.ckan_config("package_edit_return_url", None)