From 1bab3416915f4cb6ac53d62c343229eb59acffd4 Mon Sep 17 00:00:00 2001 From: Timothy Oehrlein Date: Sat, 31 Oct 2020 02:49:29 -0500 Subject: [PATCH 1/5] Updated ObjectField to allow for unmapped keys --- django_elasticsearch_dsl/fields.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/django_elasticsearch_dsl/fields.py b/django_elasticsearch_dsl/fields.py index 4c45501a..086192f2 100644 --- a/django_elasticsearch_dsl/fields.py +++ b/django_elasticsearch_dsl/fields.py @@ -115,6 +115,9 @@ def _get_inner_field_data(self, obj, field_value_to_ignore=None): obj, field_value_to_ignore ) + if not data and obj and isinstance(obj, dict): + data = obj + return data def get_value_from_instance(self, instance, field_value_to_ignore=None): @@ -125,7 +128,7 @@ def get_value_from_instance(self, instance, field_value_to_ignore=None): if objs is None: return {} try: - is_iterable = bool(iter(objs)) + is_iterable = bool(iter(objs)) and not isinstance(objs, dict) except TypeError: is_iterable = False From 87d8e661221a88a684f27fdf3e7f887574a36188 Mon Sep 17 00:00:00 2001 From: Timothy Oehrlein Date: Sat, 31 Oct 2020 14:57:50 -0500 Subject: [PATCH 2/5] Moved is_iterable dict check --- django_elasticsearch_dsl/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django_elasticsearch_dsl/fields.py b/django_elasticsearch_dsl/fields.py index 086192f2..52ba9c6a 100644 --- a/django_elasticsearch_dsl/fields.py +++ b/django_elasticsearch_dsl/fields.py @@ -128,11 +128,11 @@ def get_value_from_instance(self, instance, field_value_to_ignore=None): if objs is None: return {} try: - is_iterable = bool(iter(objs)) and not isinstance(objs, dict) + is_iterable = bool(iter(objs)) except TypeError: is_iterable = False - if is_iterable: + if is_iterable and not isinstance(objs, dict): return [ self._get_inner_field_data(obj, field_value_to_ignore) for obj in objs if obj != field_value_to_ignore From 22dac7a7b6d2bae181902f1dc45fd5b95242103c Mon Sep 17 00:00:00 2001 From: oehrlein Date: Sat, 31 Oct 2020 20:33:16 -0500 Subject: [PATCH 3/5] Removed unneeded space at start of line --- django_elasticsearch_dsl/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_elasticsearch_dsl/fields.py b/django_elasticsearch_dsl/fields.py index 52ba9c6a..cabed556 100644 --- a/django_elasticsearch_dsl/fields.py +++ b/django_elasticsearch_dsl/fields.py @@ -131,7 +131,7 @@ def get_value_from_instance(self, instance, field_value_to_ignore=None): is_iterable = bool(iter(objs)) except TypeError: is_iterable = False - + if is_iterable and not isinstance(objs, dict): return [ self._get_inner_field_data(obj, field_value_to_ignore) From 31c885782b069c30e1be613a141f2ddc0ea54956 Mon Sep 17 00:00:00 2001 From: oehrlein Date: Sat, 31 Oct 2020 21:19:04 -0500 Subject: [PATCH 4/5] Added tests for ObjectFields --- tests/test_fields.py | 77 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/test_fields.py b/tests/test_fields.py index 8e3f5d85..2f1e7266 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -100,6 +100,36 @@ def test_get_value_from_instance(self): 'last_name': "bar", }) + def test_get_value_from_instance_with_partial_properties(self): + field = ObjectField( + attr='person', + properties={ + 'first_name': TextField(analyzer='foo') + } + ) + + instance = NonCallableMock( + person=NonCallableMock(first_name='foo', last_name='bar') + ) + + self.assertEqual(field.get_value_from_instance(instance), { + 'first_name': "foo" + }) + + def test_get_value_from_instance_without_properties(self): + field = ObjectField(attr='person') + + instance = NonCallableMock( + person={'first_name': 'foo', 'last_name': 'bar'} + ) + + self.assertEqual(field.get_value_from_instance(instance), + { + 'first_name': "foo", + 'last_name': "bar" + } + ) + def test_get_value_from_instance_with_inner_objectfield(self): field = ObjectField(attr='person', properties={ 'first_name': TextField(analyzier='foo'), @@ -120,6 +150,30 @@ def test_get_value_from_instance_with_inner_objectfield(self): 'aditional': {'age': 12} }) + def test_get_value_from_instance_with_inner_objectfield_without_properties(self): + field = ObjectField( + attr='person', + properties={ + 'first_name': TextField(analyzer='foo'), + 'last_name': TextField(), + 'additional': ObjectField() + } + ) + + instance = NonCallableMock(person=NonCallableMock( + first_name="foo", + last_name="bar", + additional={'age': 12} + )) + + self.assertEqual(field.get_value_from_instance(instance), + { + 'first_name': "foo", + 'last_name': "bar", + 'additional': {'age': 12} + } + ) + def test_get_value_from_instance_with_none_inner_objectfield(self): field = ObjectField(attr='person', properties={ 'first_name': TextField(analyzier='foo'), @@ -168,6 +222,29 @@ def test_get_value_from_iterable(self): } ]) + def test_get_value_from_iterable_without_properties(self): + field = ObjectField(attr='person') + + instance = NonCallableMock( + person=[ + {'first_name': "foo1", 'last_name': "bar1"}, + {'first_name': "foo2", 'last_name': "bar2"} + ] + ) + + self.assertEqual(field.get_value_from_instance(instance), + [ + { + 'first_name': "foo1", + 'last_name': "bar1", + }, + { + 'first_name': "foo2", + 'last_name': "bar2", + } + ] + ) + class NestedFieldTestCase(TestCase): def test_get_mapping(self): From 7e06f88dd2764f19ae02bb4b21f178258345e9f6 Mon Sep 17 00:00:00 2001 From: oehrlein Date: Sun, 1 Nov 2020 00:25:52 -0500 Subject: [PATCH 5/5] Added comments explaining ObjectField changes --- django_elasticsearch_dsl/fields.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/django_elasticsearch_dsl/fields.py b/django_elasticsearch_dsl/fields.py index cabed556..82945187 100644 --- a/django_elasticsearch_dsl/fields.py +++ b/django_elasticsearch_dsl/fields.py @@ -115,6 +115,8 @@ def _get_inner_field_data(self, obj, field_value_to_ignore=None): obj, field_value_to_ignore ) + # This allows for ObjectFields to be indexed from dicts with + # dynamic keys (i.e. keys/fields not defined in 'properties') if not data and obj and isinstance(obj, dict): data = obj @@ -132,6 +134,8 @@ def get_value_from_instance(self, instance, field_value_to_ignore=None): except TypeError: is_iterable = False + # While dicts are iterable, they need to be excluded here so + # their full data is indexed if is_iterable and not isinstance(objs, dict): return [ self._get_inner_field_data(obj, field_value_to_ignore)