From 9d6611d96a267750cac1155aa481b28a5bbe67eb Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Thu, 7 Feb 2019 14:49:09 +0100
Subject: [PATCH 01/12] update: add a test for PUT. But it's passing...

---
 djangoldp/tests/runner.py                   |  5 +++-
 djangoldp/tests/{tests.py => tests_save.py} | 27 ++++++++++++++++-
 djangoldp/tests/tests_update.py             | 32 +++++++++++++++++++++
 3 files changed, 62 insertions(+), 2 deletions(-)
 rename djangoldp/tests/{tests.py => tests_save.py} (61%)
 create mode 100644 djangoldp/tests/tests_update.py

diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py
index 8e043144..1cf8604d 100644
--- a/djangoldp/tests/runner.py
+++ b/djangoldp/tests/runner.py
@@ -24,6 +24,9 @@ from django.test.runner import DiscoverRunner
 
 test_runner = DiscoverRunner(verbosity=1)
 
-failures = test_runner.run_tests(['djangoldp.tests.tests'])
+failures = test_runner.run_tests([
+    'djangoldp.tests.tests_save',
+    'djangoldp.tests.tests_update'])
 if failures:
     sys.exit(failures)
+
diff --git a/djangoldp/tests/tests.py b/djangoldp/tests/tests_save.py
similarity index 61%
rename from djangoldp/tests/tests.py
rename to djangoldp/tests/tests_save.py
index 647b26c3..e4108d1f 100644
--- a/djangoldp/tests/tests.py
+++ b/djangoldp/tests/tests_save.py
@@ -6,7 +6,7 @@ from djangoldp.tests.models import Skill, JobOffer
 
 class Serializer(TestCase):
 
-    def test_container_serializer_save(self):
+    def test_save_m2m(self):
         skill1 = Skill.objects.create(title="skill1")
         skill2 = Skill.objects.create(title="skill2")
         job = {"title": "job test",
@@ -43,3 +43,28 @@ class Serializer(TestCase):
 
         self.assertEquals(result.title, "job test")
         self.assertIs(result.skills.count(), 0)
+
+    def test_update(self):
+        skill1 = Skill.objects.create(title="skill1")
+        skill2 = Skill.objects.create(title="skill2")
+        job1 = JobOffer.objects.create(title="job test")
+
+        job = {"@id": "https://happy-dev.fr/skills/{}/".format(job1.pk),
+               "title": "job test updated",
+               "skills": {
+                   "ldp:contains": [
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
+                   ]}
+               }
+
+        meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")}
+
+        meta_class = type('Meta', (), meta_args)
+        serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
+        serializer = serializer_class(data=job)
+        serializer.is_valid()
+        result = serializer.save()
+
+        self.assertEquals(result.title, "job test updated")
+        self.assertIs(result.skills.count(), 0)
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
new file mode 100644
index 00000000..f2606c03
--- /dev/null
+++ b/djangoldp/tests/tests_update.py
@@ -0,0 +1,32 @@
+from django.test import TestCase
+
+from djangoldp.serializers import LDPSerializer
+from djangoldp.tests.models import Skill, JobOffer
+
+
+class Serializer(TestCase):
+
+    def test_update(self):
+        skill1 = Skill.objects.create(title="skill1")
+        skill2 = Skill.objects.create(title="skill2")
+        job1 = JobOffer.objects.create(title="job test")
+
+        job = {"@id": "https://happy-dev.fr/skills/{}/".format(job1.pk),
+               "title": "job test updated",
+               "skills": {
+                   "ldp:contains": [
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
+                   ]}
+               }
+
+        meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")}
+
+        meta_class = type('Meta', (), meta_args)
+        serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
+        serializer = serializer_class(data=job)
+        serializer.is_valid()
+        result = serializer.save()
+
+        self.assertEquals(result.title, "job test updated")
+        self.assertIs(result.skills.count(), 2)
-- 
GitLab


From e9a0ec9e8b852313e2c31fc5403d0355025b1962 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Sun, 10 Feb 2019 11:16:36 +0100
Subject: [PATCH 02/12] fix: PUT should work with flatten JSonLD input

---
 djangoldp/serializers.py        | 55 ++++++++++++++++++++++++++++++++-
 djangoldp/tests/tests_save.py   | 24 --------------
 djangoldp/tests/tests_update.py | 30 ++++++++++++++++--
 3 files changed, 82 insertions(+), 27 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 6ce27477..603ac7b3 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -65,6 +65,12 @@ class JsonLdField(HyperlinkedRelatedField):
         except MultiValueDictKeyError:
             pass
 
+    def to_internal_value(self, data):
+        return super().to_internal_value(data)
+
+    def get_value(self, dictionary):
+        return super().get_value(dictionary)
+
 
 class JsonLdRelatedField(JsonLdField):
     def to_representation(self, value):
@@ -103,12 +109,19 @@ class JsonLdIdentityField(JsonLdField):
         except:
             return super().to_internal_value(data)
 
+    def get_value(self, dictionary):
+        return super().get_value(dictionary)
+
 
 class LDPSerializer(HyperlinkedModelSerializer):
     url_field_name = "@id"
     serializer_related_field = JsonLdRelatedField
     serializer_url_field = JsonLdIdentityField
 
+    @property
+    def data(self):
+        return super().data
+
     def get_default_field_names(self, declared_fields, model_info):
         try:
             fields = list(self.Meta.model._meta.serializer_fields)
@@ -130,6 +143,27 @@ class LDPSerializer(HyperlinkedModelSerializer):
                                get_perms(self.context['request'].user, obj)]
         return data
 
+    def build_standard_field(self, field_name, model_field):
+        class JSonLDStandardField:
+            parent_view_name = None
+
+            def __init__(self, **kwargs):
+                self.parent_view_name = kwargs.pop('parent_view_name')
+                super().__init__(**kwargs)
+
+            def get_value(self, dictionary):
+                try:
+                    object_list = dictionary["@graph"]
+                    part_id = '/{}'.format(get_resolver().reverse_dict[self.parent_view_name][0][0][0], self.parent.instance.pk)
+                    obj = next(filter(lambda o: part_id in o['@id'], object_list))
+                    return super().get_value(obj)
+                except KeyError:
+                    return super().get_value(dictionary)
+
+        field_class, field_kwargs = super().build_standard_field(field_name, model_field)
+        field_kwargs['parent_view_name'] = '{}-list'.format(model_field.model._meta.object_name.lower())
+        return type(field_class.__name__ + 'Valued', (JSonLDStandardField, field_class),  {}), field_kwargs
+
     def build_nested_field(self, field_name, relation_info, nested_depth):
         class NestedLDPSerializer(self.__class__):
 
@@ -147,6 +181,9 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     view_name='{}-detail'.format(model._meta.object_name.lower()),
                     queryset=model.objects.all()).to_internal_value(data)
 
+            def get_value(self, dictionary):
+                return super().get_value(dictionary)
+
         kwargs = get_nested_relation_kwargs(relation_info)
         kwargs['read_only'] = False
         kwargs['required'] = False
@@ -154,7 +191,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
     @classmethod
     def many_init(cls, *args, **kwargs):
-        kwargs['child'] = cls()
+        kwargs['child'] = cls(**kwargs)
         try:
             cls.Meta.depth = kwargs['context']['view'].many_depth
         except KeyError:
@@ -174,3 +211,19 @@ class LDPSerializer(HyperlinkedModelSerializer):
                 getattr(obj, field_name).add(item)
 
         return obj
+
+    def update(self, instance, validated_data):
+        nested_fields = []
+        nested_fields_name = list(filter(lambda key: isinstance(validated_data[key], list), validated_data))
+        for field_name in nested_fields_name:
+            nested_fields.append((field_name, validated_data.pop(field_name)))
+
+        for attr, value in validated_data.items():
+             setattr(instance, attr, value)
+        instance.save()
+
+        for (field_name, data) in nested_fields:
+            for item in data:
+                getattr(instance, field_name).add(item)
+
+        return instance
diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py
index e4108d1f..53f48e21 100644
--- a/djangoldp/tests/tests_save.py
+++ b/djangoldp/tests/tests_save.py
@@ -44,27 +44,3 @@ class Serializer(TestCase):
         self.assertEquals(result.title, "job test")
         self.assertIs(result.skills.count(), 0)
 
-    def test_update(self):
-        skill1 = Skill.objects.create(title="skill1")
-        skill2 = Skill.objects.create(title="skill2")
-        job1 = JobOffer.objects.create(title="job test")
-
-        job = {"@id": "https://happy-dev.fr/skills/{}/".format(job1.pk),
-               "title": "job test updated",
-               "skills": {
-                   "ldp:contains": [
-                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
-                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
-                   ]}
-               }
-
-        meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")}
-
-        meta_class = type('Meta', (), meta_args)
-        serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
-        serializer = serializer_class(data=job)
-        serializer.is_valid()
-        result = serializer.save()
-
-        self.assertEquals(result.title, "job test updated")
-        self.assertIs(result.skills.count(), 0)
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index f2606c03..c3b65b5c 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -11,7 +11,7 @@ class Serializer(TestCase):
         skill2 = Skill.objects.create(title="skill2")
         job1 = JobOffer.objects.create(title="job test")
 
-        job = {"@id": "https://happy-dev.fr/skills/{}/".format(job1.pk),
+        job = {"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.pk),
                "title": "job test updated",
                "skills": {
                    "ldp:contains": [
@@ -24,9 +24,35 @@ class Serializer(TestCase):
 
         meta_class = type('Meta', (), meta_args)
         serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
-        serializer = serializer_class(data=job)
+        serializer = serializer_class(data=job, instance=job1)
         serializer.is_valid()
         result = serializer.save()
 
         self.assertEquals(result.title, "job test updated")
         self.assertIs(result.skills.count(), 2)
+
+    def test_update_graph(self):
+        skill1 = Skill.objects.create(title="skill1")
+        skill2 = Skill.objects.create(title="skill2")
+        job1 = JobOffer.objects.create(title="job test")
+
+        job = {"@graph": [{"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.pk),
+               "title": "job test updated",
+               "skills": {
+                   "ldp:contains": [
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
+                   ]}
+               }]
+            }
+
+        meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")}
+
+        meta_class = type('Meta', (), meta_args)
+        serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class})
+        serializer = serializer_class(data=job, instance=job1)
+        serializer.is_valid()
+        result = serializer.save()
+
+        self.assertEquals(result.title, "job test updated")
+        #self.assertIs(result.skills.count(), 2)
-- 
GitLab


From 7417cb7d1160d6a590743a20531f2a26c82f13fa Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Mon, 11 Feb 2019 11:20:03 +0100
Subject: [PATCH 03/12] fix: PUT should save nested relations

---
 djangoldp/serializers.py        | 13 +++++++++++++
 djangoldp/tests/tests_update.py |  2 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 603ac7b3..740a8b37 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -30,6 +30,16 @@ class LDListMixin:
         self.id = parent_id + self.field_name + "/"
         return super().get_attribute(instance)
 
+    def get_value(self, dictionary):
+        try:
+            object_list = dictionary["@graph"]
+            view_name = '{}-list'.format(self.parent.Meta.model._meta.object_name.lower())
+            part_id = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][0], self.parent.instance.pk)
+            obj = next(filter(lambda o: part_id in o['@id'], object_list))
+            return super().get_value(obj)
+        except KeyError:
+            return super().get_value(dictionary)
+
 
 class ContainerSerializer(LDListMixin, ListSerializer):
     id = ''
@@ -198,6 +208,9 @@ class LDPSerializer(HyperlinkedModelSerializer):
             pass
         return ContainerSerializer(*args, **kwargs)
 
+    def get_value(self, dictionary):
+        return super().get_value(dictionary)
+
     def create(self, validated_data):
         nested_fields = []
         nested_fields_name = list(filter(lambda key: isinstance(validated_data[key], list), validated_data))
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index c3b65b5c..300f2eed 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -55,4 +55,4 @@ class Serializer(TestCase):
         result = serializer.save()
 
         self.assertEquals(result.title, "job test updated")
-        #self.assertIs(result.skills.count(), 2)
+        self.assertIs(result.skills.count(), 2)
-- 
GitLab


From 70607de5748d1fb6bcf5aa50b73cc503d216642f Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Mon, 11 Feb 2019 21:37:07 +0100
Subject: [PATCH 04/12] update: add failing test for nested updates

---
 djangoldp/serializers.py        |  4 ++--
 djangoldp/tests/tests_update.py | 35 +++++++++++++++++++++++++--------
 2 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 740a8b37..0f6d711a 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -92,7 +92,7 @@ class JsonLdRelatedField(JsonLdField):
     def to_internal_value(self, data):
         try:
             return super().to_internal_value(data['@id'])
-        except:
+        except KeyError:
             return super().to_internal_value(data)
 
     @classmethod
@@ -116,7 +116,7 @@ class JsonLdIdentityField(JsonLdField):
     def to_internal_value(self, data):
         try:
             return super().to_internal_value(data['@id'])
-        except:
+        except KeyError:
             return super().to_internal_value(data)
 
     def get_value(self, dictionary):
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index 300f2eed..df79b949 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -15,8 +15,9 @@ class Serializer(TestCase):
                "title": "job test updated",
                "skills": {
                    "ldp:contains": [
+                       # {"title": "new skill"},
                        {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
-                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk), "title": "skill2 UP"},
                    ]}
                }
 
@@ -30,6 +31,9 @@ class Serializer(TestCase):
 
         self.assertEquals(result.title, "job test updated")
         self.assertIs(result.skills.count(), 2)
+        # self.assertEquals(result.skills[0].title, "new skill")  # new skill
+        self.assertEquals(result.skills.all()[0].title, "skill1")     # no change
+        self.assertEquals(result.skills.all()[1].title, "skill2 UP")  # title updated
 
     def test_update_graph(self):
         skill1 = Skill.objects.create(title="skill1")
@@ -37,13 +41,25 @@ class Serializer(TestCase):
         job1 = JobOffer.objects.create(title="job test")
 
         job = {"@graph": [{"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.pk),
-               "title": "job test updated",
-               "skills": {
-                   "ldp:contains": [
-                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
-                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
-                   ]}
-               }]
+                           "title": "job test updated",
+                           "skills": {
+                               "ldp:contains": [
+                                   {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
+                                   {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
+                                   # {"@id": "_.123"},
+                               ]}
+                           },
+                          # {
+                          #    "@id": "_.123",
+                          #     "title": "new skill"
+                          # },
+                          {
+                              "@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk),
+                          },
+                          {
+                              "@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk),
+                              "title": "skill2 UP"
+                          }]
             }
 
         meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")}
@@ -56,3 +72,6 @@ class Serializer(TestCase):
 
         self.assertEquals(result.title, "job test updated")
         self.assertIs(result.skills.count(), 2)
+        #self.assertEquals(result.skills[0].title, "new skill")  # new skill
+        self.assertEquals(result.skills.all()[0].title, "skill1")     # no change
+        self.assertEquals(result.skills.all()[1].title, "skill2 UP")  # title updated
-- 
GitLab


From 28d878916032a4fb487b5489bfd61997945adbe2 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Mon, 11 Feb 2019 23:37:09 +0100
Subject: [PATCH 05/12] fix: allow update on nested fields on POST or PUT

---
 djangoldp/serializers.py        | 13 +++++++++++--
 djangoldp/tests/tests_save.py   |  5 ++++-
 djangoldp/tests/tests_update.py |  2 ++
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 0f6d711a..02b96e28 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -14,7 +14,7 @@ class LDListMixin:
         # data = json.loads(data)
         try:
             data = data['ldp:contains']
-        except TypeError:
+        except (TypeError, KeyError):
             pass
         if isinstance(data, dict):
             data = [data]
@@ -187,9 +187,12 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
             def to_internal_value(self, data):
                 model = self.Meta.model
-                return self.serializer_related_field(
+                instance = self.serializer_related_field(
                     view_name='{}-detail'.format(model._meta.object_name.lower()),
                     queryset=model.objects.all()).to_internal_value(data)
+                for key in data:
+                    setattr(instance, key, data[key])
+                return instance
 
             def get_value(self, dictionary):
                 return super().get_value(dictionary)
@@ -221,6 +224,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
         for (field_name, data) in nested_fields:
             for item in data:
+                item.save()
                 getattr(obj, field_name).add(item)
 
         return obj
@@ -236,7 +240,12 @@ class LDPSerializer(HyperlinkedModelSerializer):
         instance.save()
 
         for (field_name, data) in nested_fields:
+            try:
+                getattr(instance, field_name).clear()
+            except AttributeError:
+                pass
             for item in data:
+                item.save()
                 getattr(instance, field_name).add(item)
 
         return instance
diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py
index 53f48e21..4375f943 100644
--- a/djangoldp/tests/tests_save.py
+++ b/djangoldp/tests/tests_save.py
@@ -9,11 +9,12 @@ class Serializer(TestCase):
     def test_save_m2m(self):
         skill1 = Skill.objects.create(title="skill1")
         skill2 = Skill.objects.create(title="skill2")
+
         job = {"title": "job test",
                "skills": {
                    "ldp:contains": [
                        {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
-                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
+                       {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk), "title": "skill2 UP"},
                    ]}
                }
 
@@ -27,6 +28,8 @@ class Serializer(TestCase):
 
         self.assertEquals(result.title, "job test")
         self.assertIs(result.skills.count(), 2)
+        self.assertEquals(result.skills.all()[0].title, "skill1")     # no change
+        self.assertEquals(result.skills.all()[1].title, "skill2 UP")  # title updated
 
     def test_save_without_nested_fields(self):
         skill1 = Skill.objects.create(title="skill1")
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index df79b949..1b84325c 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -7,9 +7,11 @@ from djangoldp.tests.models import Skill, JobOffer
 class Serializer(TestCase):
 
     def test_update(self):
+        skill = Skill.objects.create(title="to drop")
         skill1 = Skill.objects.create(title="skill1")
         skill2 = Skill.objects.create(title="skill2")
         job1 = JobOffer.objects.create(title="job test")
+        job1.skills.add(skill)
 
         job = {"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.pk),
                "title": "job test updated",
-- 
GitLab


From fcd84309de868f5bde8ce6c1d23d1b879e308354 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Tue, 12 Feb 2019 13:00:32 +0100
Subject: [PATCH 06/12] update: allow nested object creation on PUT request

---
 djangoldp/serializers.py        | 50 +++++++++++++++++++++++----------
 djangoldp/tests/tests_update.py | 13 +++++----
 2 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 02b96e28..96f2c82e 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -1,8 +1,10 @@
+from urllib import parse
+
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import get_resolver
+from django.core.urlresolvers import get_resolver, resolve, get_script_prefix
 from django.utils.datastructures import MultiValueDictKeyError
+from django.utils.encoding import uri_to_iri
 from guardian.shortcuts import get_perms
-from rest_framework.fields import empty
 from rest_framework.relations import HyperlinkedRelatedField, ManyRelatedField, MANY_RELATION_KWARGS
 from rest_framework.serializers import HyperlinkedModelSerializer, ListSerializer
 from rest_framework.utils.field_mapping import get_nested_relation_kwargs
@@ -164,7 +166,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
             def get_value(self, dictionary):
                 try:
                     object_list = dictionary["@graph"]
-                    part_id = '/{}'.format(get_resolver().reverse_dict[self.parent_view_name][0][0][0], self.parent.instance.pk)
+                    part_id = '/{}'.format(get_resolver().reverse_dict[self.parent_view_name][0][0][0],
+                                           self.parent.instance.pk)
                     obj = next(filter(lambda o: part_id in o['@id'], object_list))
                     return super().get_value(obj)
                 except KeyError:
@@ -172,7 +175,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
         field_class, field_kwargs = super().build_standard_field(field_name, model_field)
         field_kwargs['parent_view_name'] = '{}-list'.format(model_field.model._meta.object_name.lower())
-        return type(field_class.__name__ + 'Valued', (JSonLDStandardField, field_class),  {}), field_kwargs
+        return type(field_class.__name__ + 'Valued', (JSonLDStandardField, field_class), {}), field_kwargs
 
     def build_nested_field(self, field_name, relation_info, nested_depth):
         class NestedLDPSerializer(self.__class__):
@@ -186,13 +189,21 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     fields = '__all__'
 
             def to_internal_value(self, data):
-                model = self.Meta.model
-                instance = self.serializer_related_field(
-                    view_name='{}-detail'.format(model._meta.object_name.lower()),
-                    queryset=model.objects.all()).to_internal_value(data)
-                for key in data:
-                    setattr(instance, key, data[key])
-                return instance
+                value = super().to_internal_value(data)
+                if '@id' in data:
+                    uri = data['@id']
+                    http_prefix = uri.startswith(('http:', 'https:'))
+
+                    if http_prefix:
+                        uri = parse.urlparse(uri).path
+                        prefix = get_script_prefix()
+                        if uri.startswith(prefix):
+                            uri = '/' + uri[len(prefix):]
+
+                    match = resolve(uri_to_iri(uri))
+                    value['pk'] = match.kwargs['pk']
+
+                return value
 
             def get_value(self, dictionary):
                 return super().get_value(dictionary)
@@ -215,12 +226,15 @@ class LDPSerializer(HyperlinkedModelSerializer):
         return super().get_value(dictionary)
 
     def create(self, validated_data):
+        return self.internal_create(validated_data, model=self.Meta.model)
+
+    def internal_create(self, validated_data, model):
         nested_fields = []
         nested_fields_name = list(filter(lambda key: isinstance(validated_data[key], list), validated_data))
         for field_name in nested_fields_name:
             nested_fields.append((field_name, validated_data.pop(field_name)))
 
-        obj = self.Meta.model.objects.create(**validated_data)
+        obj = model.objects.create(**validated_data)
 
         for (field_name, data) in nested_fields:
             for item in data:
@@ -236,7 +250,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
             nested_fields.append((field_name, validated_data.pop(field_name)))
 
         for attr, value in validated_data.items():
-             setattr(instance, attr, value)
+            setattr(instance, attr, value)
         instance.save()
 
         for (field_name, data) in nested_fields:
@@ -245,7 +259,13 @@ class LDPSerializer(HyperlinkedModelSerializer):
             except AttributeError:
                 pass
             for item in data:
-                item.save()
-                getattr(instance, field_name).add(item)
+                manager = getattr(instance, field_name)
+                if 'pk' in item:
+                    oldObj = manager.model.objects.get(pk=item['pk'])
+                    savedItem = self.update(instance=oldObj, validated_data=item)
+                else:
+                    savedItem = self.internal_create(validated_data=item, model=manager.model)
+
+                getattr(instance, field_name).add(savedItem)
 
         return instance
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index 1b84325c..7e9e6815 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -17,7 +17,7 @@ class Serializer(TestCase):
                "title": "job test updated",
                "skills": {
                    "ldp:contains": [
-                       # {"title": "new skill"},
+                       {"title": "new skill"},
                        {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
                        {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk), "title": "skill2 UP"},
                    ]}
@@ -32,15 +32,18 @@ class Serializer(TestCase):
         result = serializer.save()
 
         self.assertEquals(result.title, "job test updated")
-        self.assertIs(result.skills.count(), 2)
-        # self.assertEquals(result.skills[0].title, "new skill")  # new skill
-        self.assertEquals(result.skills.all()[0].title, "skill1")     # no change
-        self.assertEquals(result.skills.all()[1].title, "skill2 UP")  # title updated
+        self.assertIs(result.skills.count(), 3)
+        skills = result.skills.all().order_by('title');
+        self.assertEquals(skills[0].title, "new skill")  # new skill
+        self.assertEquals(skills[1].title, "skill1")     # no change
+        self.assertEquals(skills[2].title, "skill2 UP")  # title updated
 
     def test_update_graph(self):
+        skill = Skill.objects.create(title="to drop")
         skill1 = Skill.objects.create(title="skill1")
         skill2 = Skill.objects.create(title="skill2")
         job1 = JobOffer.objects.create(title="job test")
+        job1.skills.add(skill)
 
         job = {"@graph": [{"@id": "https://happy-dev.fr/job-offers/{}/".format(job1.pk),
                            "title": "job test updated",
-- 
GitLab


From 9e98986d25d139f479506d00ba255082602660ff Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Tue, 12 Feb 2019 13:05:06 +0100
Subject: [PATCH 07/12] update: allow nested object creation on POST

---
 djangoldp/serializers.py | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 96f2c82e..ab70d648 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -234,14 +234,11 @@ class LDPSerializer(HyperlinkedModelSerializer):
         for field_name in nested_fields_name:
             nested_fields.append((field_name, validated_data.pop(field_name)))
 
-        obj = model.objects.create(**validated_data)
+        instance = model.objects.create(**validated_data)
 
-        for (field_name, data) in nested_fields:
-            for item in data:
-                item.save()
-                getattr(obj, field_name).add(item)
+        self.save_or_update_nested(instance, nested_fields)
 
-        return obj
+        return instance
 
     def update(self, instance, validated_data):
         nested_fields = []
@@ -253,6 +250,11 @@ class LDPSerializer(HyperlinkedModelSerializer):
             setattr(instance, attr, value)
         instance.save()
 
+        self.save_or_update_nested(instance, nested_fields)
+
+        return instance
+
+    def save_or_update_nested(self, instance, nested_fields):
         for (field_name, data) in nested_fields:
             try:
                 getattr(instance, field_name).clear()
@@ -267,5 +269,3 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     savedItem = self.internal_create(validated_data=item, model=manager.model)
 
                 getattr(instance, field_name).add(savedItem)
-
-        return instance
-- 
GitLab


From 20a9b245f784e93f4b1fb4cef7fccf65bf515a94 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Tue, 12 Feb 2019 14:39:36 +0100
Subject: [PATCH 08/12] update: allow update and create on PUT or POST with
 @graph input

---
 djangoldp/serializers.py | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index ab70d648..70dc8ac7 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -38,7 +38,21 @@ class LDListMixin:
             view_name = '{}-list'.format(self.parent.Meta.model._meta.object_name.lower())
             part_id = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][0], self.parent.instance.pk)
             obj = next(filter(lambda o: part_id in o['@id'], object_list))
-            return super().get_value(obj)
+            list = super().get_value(obj);
+            try:
+                list= list['ldp:contains']
+            except KeyError:
+                pass
+
+            ret=[]
+            for item in list:
+                fullItem = next(filter(lambda o: item['@id'] == o['@id'], object_list))
+                if fullItem is None:
+                    ret.append(item)
+                else:
+                    ret.append(fullItem)
+
+            return ret
         except KeyError:
             return super().get_value(dictionary)
 
-- 
GitLab


From 2217a972f29896cf60c1b104d996b112a64dfc4d Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Tue, 12 Feb 2019 15:28:41 +0100
Subject: [PATCH 09/12] fix: add new nested object with @graph representation

---
 djangoldp/serializers.py        | 18 ++++++++++++++----
 djangoldp/tests/tests_update.py | 22 ++++++++++++----------
 2 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 70dc8ac7..857d5f1a 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -1,7 +1,7 @@
 from urllib import parse
 
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import get_resolver, resolve, get_script_prefix
+from django.core.urlresolvers import get_resolver, resolve, get_script_prefix, Resolver404
 from django.utils.datastructures import MultiValueDictKeyError
 from django.utils.encoding import uri_to_iri
 from guardian.shortcuts import get_perms
@@ -44,9 +44,16 @@ class LDListMixin:
             except KeyError:
                 pass
 
+            if isinstance(list, dict):
+                list = [list]
+
             ret=[]
             for item in list:
-                fullItem = next(filter(lambda o: item['@id'] == o['@id'], object_list))
+                fullItem=None
+                try:
+                    fullItem = next(filter(lambda o: item['@id'] == o['@id'], object_list))
+                except StopIteration:
+                    pass
                 if fullItem is None:
                     ret.append(item)
                 else:
@@ -214,8 +221,11 @@ class LDPSerializer(HyperlinkedModelSerializer):
                         if uri.startswith(prefix):
                             uri = '/' + uri[len(prefix):]
 
-                    match = resolve(uri_to_iri(uri))
-                    value['pk'] = match.kwargs['pk']
+                    try:
+                        match = resolve(uri_to_iri(uri))
+                        value['pk'] = match.kwargs['pk']
+                    except Resolver404:
+                        pass
 
                 return value
 
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index 7e9e6815..e81a4eaf 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -33,7 +33,7 @@ class Serializer(TestCase):
 
         self.assertEquals(result.title, "job test updated")
         self.assertIs(result.skills.count(), 3)
-        skills = result.skills.all().order_by('title');
+        skills = result.skills.all().order_by('title')
         self.assertEquals(skills[0].title, "new skill")  # new skill
         self.assertEquals(skills[1].title, "skill1")     # no change
         self.assertEquals(skills[2].title, "skill2 UP")  # title updated
@@ -51,13 +51,13 @@ class Serializer(TestCase):
                                "ldp:contains": [
                                    {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
                                    {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)},
-                                   # {"@id": "_.123"},
+                                   {"@id": "_.123"},
                                ]}
                            },
-                          # {
-                          #    "@id": "_.123",
-                          #     "title": "new skill"
-                          # },
+                          {
+                             "@id": "_.123",
+                              "title": "new skill"
+                          },
                           {
                               "@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk),
                           },
@@ -75,8 +75,10 @@ class Serializer(TestCase):
         serializer.is_valid()
         result = serializer.save()
 
+        skills = result.skills.all().order_by('title')
+
         self.assertEquals(result.title, "job test updated")
-        self.assertIs(result.skills.count(), 2)
-        #self.assertEquals(result.skills[0].title, "new skill")  # new skill
-        self.assertEquals(result.skills.all()[0].title, "skill1")     # no change
-        self.assertEquals(result.skills.all()[1].title, "skill2 UP")  # title updated
+        self.assertIs(result.skills.count(), 3)
+        self.assertEquals(skills[0].title, "new skill")  # new skill
+        self.assertEquals(skills[1].title, "skill1")     # no change
+        self.assertEquals(skills[2].title, "skill2 UP")  # title updated
-- 
GitLab


From 7602097912c738f5d4a581c7829518ab01845284 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Tue, 12 Feb 2019 15:57:06 +0100
Subject: [PATCH 10/12] update: deal with required value on partial update

---
 djangoldp/serializers.py        | 20 +++++++++++---------
 djangoldp/tests/models.py       |  1 +
 djangoldp/tests/tests_save.py   |  8 ++++----
 djangoldp/tests/tests_update.py | 17 +++++++++--------
 4 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 857d5f1a..82c031d8 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -49,15 +49,15 @@ class LDListMixin:
 
             ret=[]
             for item in list:
-                fullItem=None
+                full_item=None
                 try:
-                    fullItem = next(filter(lambda o: item['@id'] == o['@id'], object_list))
+                    full_item = next(filter(lambda o: item['@id'] == o['@id'], object_list))
                 except StopIteration:
                     pass
-                if fullItem is None:
+                if full_item is None:
                     ret.append(item)
                 else:
-                    ret.append(fullItem)
+                    ret.append(full_item)
 
             return ret
         except KeyError:
@@ -210,9 +210,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     fields = '__all__'
 
             def to_internal_value(self, data):
-                value = super().to_internal_value(data)
-                if '@id' in data:
-                    uri = data['@id']
+                if self.url_field_name in data:
+                    uri = data[self.url_field_name]
                     http_prefix = uri.startswith(('http:', 'https:'))
 
                     if http_prefix:
@@ -223,11 +222,14 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
                     try:
                         match = resolve(uri_to_iri(uri))
-                        value['pk'] = match.kwargs['pk']
+                        instance = self.Meta.model.objects.get(pk=match.kwargs['pk'])
+                        for key in self.data:
+                            if not key in data:
+                                data[key] = getattr(instance, key)
                     except Resolver404:
                         pass
 
-                return value
+                return super().to_internal_value(data)
 
             def get_value(self, dictionary):
                 return super().get_value(dictionary)
diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py
index d8e0e76b..968ffbbb 100644
--- a/djangoldp/tests/models.py
+++ b/djangoldp/tests/models.py
@@ -4,6 +4,7 @@ from django.db import models
 
 class Skill(models.Model):
     title = models.CharField(max_length=255, blank=True, null=True)
+    obligatoire = models.CharField(max_length=255)
 
 
 class JobOffer(models.Model):
diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py
index 4375f943..1117c928 100644
--- a/djangoldp/tests/tests_save.py
+++ b/djangoldp/tests/tests_save.py
@@ -7,8 +7,8 @@ from djangoldp.tests.models import Skill, JobOffer
 class Serializer(TestCase):
 
     def test_save_m2m(self):
-        skill1 = Skill.objects.create(title="skill1")
-        skill2 = Skill.objects.create(title="skill2")
+        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire")
+        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire")
 
         job = {"title": "job test",
                "skills": {
@@ -32,8 +32,8 @@ class Serializer(TestCase):
         self.assertEquals(result.skills.all()[1].title, "skill2 UP")  # title updated
 
     def test_save_without_nested_fields(self):
-        skill1 = Skill.objects.create(title="skill1")
-        skill2 = Skill.objects.create(title="skill2")
+        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire")
+        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire")
         job = {"title": "job test"}
 
         meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")}
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index e81a4eaf..a4033acb 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -7,9 +7,9 @@ from djangoldp.tests.models import Skill, JobOffer
 class Serializer(TestCase):
 
     def test_update(self):
-        skill = Skill.objects.create(title="to drop")
-        skill1 = Skill.objects.create(title="skill1")
-        skill2 = Skill.objects.create(title="skill2")
+        skill = Skill.objects.create(title="to drop", obligatoire="obligatoire")
+        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire")
+        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire")
         job1 = JobOffer.objects.create(title="job test")
         job1.skills.add(skill)
 
@@ -17,7 +17,7 @@ class Serializer(TestCase):
                "title": "job test updated",
                "skills": {
                    "ldp:contains": [
-                       {"title": "new skill"},
+                       {"title": "new skill", "obligatoire": "okay"},
                        {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)},
                        {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk), "title": "skill2 UP"},
                    ]}
@@ -39,9 +39,9 @@ class Serializer(TestCase):
         self.assertEquals(skills[2].title, "skill2 UP")  # title updated
 
     def test_update_graph(self):
-        skill = Skill.objects.create(title="to drop")
-        skill1 = Skill.objects.create(title="skill1")
-        skill2 = Skill.objects.create(title="skill2")
+        skill = Skill.objects.create(title="to drop", obligatoire="obligatoire")
+        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire")
+        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire")
         job1 = JobOffer.objects.create(title="job test")
         job1.skills.add(skill)
 
@@ -56,7 +56,8 @@ class Serializer(TestCase):
                            },
                           {
                              "@id": "_.123",
-                              "title": "new skill"
+                              "title": "new skill",
+                              "obligatoire": "okay"
                           },
                           {
                               "@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk),
-- 
GitLab


From 2b02e3e96875e455afb8cda1574a1d1763d45cb0 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Tue, 12 Feb 2019 18:21:39 +0100
Subject: [PATCH 11/12] fix: manage simple nested field (non list)

---
 djangoldp/serializers.py | 92 +++++++++++++++++++++++++++++++++-------
 1 file changed, 76 insertions(+), 16 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 82c031d8..4f8ead62 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -1,3 +1,4 @@
+from collections import OrderedDict, Mapping
 from urllib import parse
 
 from django.core.exceptions import ImproperlyConfigured
@@ -5,8 +6,13 @@ from django.core.urlresolvers import get_resolver, resolve, get_script_prefix, R
 from django.utils.datastructures import MultiValueDictKeyError
 from django.utils.encoding import uri_to_iri
 from guardian.shortcuts import get_perms
+from django.core.exceptions import ValidationError as DjangoValidationError
+from rest_framework.exceptions import ValidationError
+from rest_framework.fields import SkipField
+from rest_framework.fields import get_error_detail, set_value
 from rest_framework.relations import HyperlinkedRelatedField, ManyRelatedField, MANY_RELATION_KWARGS
 from rest_framework.serializers import HyperlinkedModelSerializer, ListSerializer
+from rest_framework.settings import api_settings
 from rest_framework.utils.field_mapping import get_nested_relation_kwargs
 from rest_framework.utils.serializer_helpers import ReturnDict
 
@@ -40,16 +46,16 @@ class LDListMixin:
             obj = next(filter(lambda o: part_id in o['@id'], object_list))
             list = super().get_value(obj);
             try:
-                list= list['ldp:contains']
+                list = list['ldp:contains']
             except KeyError:
                 pass
 
             if isinstance(list, dict):
                 list = [list]
 
-            ret=[]
+            ret = []
             for item in list:
-                full_item=None
+                full_item = None
                 try:
                     full_item = next(filter(lambda o: item['@id'] == o['@id'], object_list))
                 except StopIteration:
@@ -77,7 +83,7 @@ class ContainerSerializer(LDListMixin, ListSerializer):
     def to_internal_value(self, data):
         try:
             return super().to_internal_value(data['@id'])
-        except:
+        except (KeyError, TypeError):
             return super().to_internal_value(data)
 
 
@@ -115,7 +121,7 @@ class JsonLdRelatedField(JsonLdField):
     def to_internal_value(self, data):
         try:
             return super().to_internal_value(data['@id'])
-        except KeyError:
+        except (KeyError, TypeError):
             return super().to_internal_value(data)
 
     @classmethod
@@ -211,6 +217,38 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
             def to_internal_value(self, data):
                 if self.url_field_name in data:
+                    if not isinstance(data, Mapping):
+                        message = self.error_messages['invalid'].format(
+                            datatype=type(data).__name__
+                        )
+                        raise ValidationError({
+                            api_settings.NON_FIELD_ERRORS_KEY: [message]
+                        }, code='invalid')
+
+                    ret = OrderedDict()
+                    errors = OrderedDict()
+                    fields = list(filter(lambda x: x.field_name in data, self._writable_fields))
+
+                    for field in fields:
+                        validate_method = getattr(self, 'validate_' + field.field_name, None)
+                        primitive_value = field.get_value(data)
+                        try:
+                            validated_value = field.run_validation(primitive_value)
+                            if validate_method is not None:
+                                validated_value = validate_method(validated_value)
+                        except ValidationError as exc:
+                            errors[field.field_name] = exc.detail
+                        except DjangoValidationError as exc:
+                            errors[field.field_name] = get_error_detail(exc)
+                        except SkipField:
+                            pass
+                        else:
+                            set_value(ret, field.source_attrs, validated_value)
+
+                    if errors:
+                        raise ValidationError(errors)
+
+
                     uri = data[self.url_field_name]
                     http_prefix = uri.startswith(('http:', 'https:'))
 
@@ -222,17 +260,14 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
                     try:
                         match = resolve(uri_to_iri(uri))
-                        instance = self.Meta.model.objects.get(pk=match.kwargs['pk'])
-                        for key in self.data:
-                            if not key in data:
-                                data[key] = getattr(instance, key)
+                        ret['pk'] = match.kwargs['pk']
                     except Resolver404:
                         pass
 
-                return super().to_internal_value(data)
+                    return ret
+                else:
+                    return super().to_internal_value(data)
 
-            def get_value(self, dictionary):
-                return super().get_value(dictionary)
 
         kwargs = get_nested_relation_kwargs(relation_info)
         kwargs['read_only'] = False
@@ -249,7 +284,25 @@ class LDPSerializer(HyperlinkedModelSerializer):
         return ContainerSerializer(*args, **kwargs)
 
     def get_value(self, dictionary):
-        return super().get_value(dictionary)
+        try:
+            object_list = dictionary["@graph"]
+            view_name = '{}-list'.format(self.parent.Meta.model._meta.object_name.lower())
+            part_id = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][0],
+                                   self.parent.instance.pk)
+            obj = next(filter(lambda o: part_id in o[self.url_field_name], object_list))
+            item = super().get_value(obj)
+            full_item = None
+            try:
+                full_item = next(filter(lambda o: item['@id'] == o['@id'], object_list))
+            except StopIteration:
+                pass
+            if full_item is None:
+                return item
+            else:
+                return full_item
+
+        except KeyError:
+            return super().get_value(dictionary)
 
     def create(self, validated_data):
         return self.internal_create(validated_data, model=self.Meta.model)
@@ -262,7 +315,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
         instance = model.objects.create(**validated_data)
 
-        self.save_or_update_nested(instance, nested_fields)
+        self.save_or_update_nested_list(instance, nested_fields)
 
         return instance
 
@@ -273,14 +326,21 @@ class LDPSerializer(HyperlinkedModelSerializer):
             nested_fields.append((field_name, validated_data.pop(field_name)))
 
         for attr, value in validated_data.items():
+            if isinstance(value, dict):
+                manager = getattr(instance, attr)
+                if 'pk' in value:
+                    oldObj = manager._meta.model.objects.get(pk=value['pk'])
+                    value = self.update(instance=oldObj, validated_data=value)
+                else:
+                    value = self.internal_create(validated_data=value, model=manager._meta.model)
             setattr(instance, attr, value)
         instance.save()
 
-        self.save_or_update_nested(instance, nested_fields)
+        self.save_or_update_nested_list(instance, nested_fields)
 
         return instance
 
-    def save_or_update_nested(self, instance, nested_fields):
+    def save_or_update_nested_list(self, instance, nested_fields):
         for (field_name, data) in nested_fields:
             try:
                 getattr(instance, field_name).clear()
-- 
GitLab


From f37f5e0ce3cbec27052620b351f93c438b8a4b15 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste <bleme@pm.me>
Date: Tue, 12 Feb 2019 20:31:54 +0100
Subject: [PATCH 12/12] syntax: reformat

---
 djangoldp/serializers.py | 4 +---
 djangoldp/views.py       | 4 ++--
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 4f8ead62..80510749 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -2,11 +2,11 @@ from collections import OrderedDict, Mapping
 from urllib import parse
 
 from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import ValidationError as DjangoValidationError
 from django.core.urlresolvers import get_resolver, resolve, get_script_prefix, Resolver404
 from django.utils.datastructures import MultiValueDictKeyError
 from django.utils.encoding import uri_to_iri
 from guardian.shortcuts import get_perms
-from django.core.exceptions import ValidationError as DjangoValidationError
 from rest_framework.exceptions import ValidationError
 from rest_framework.fields import SkipField
 from rest_framework.fields import get_error_detail, set_value
@@ -248,7 +248,6 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     if errors:
                         raise ValidationError(errors)
 
-
                     uri = data[self.url_field_name]
                     http_prefix = uri.startswith(('http:', 'https:'))
 
@@ -268,7 +267,6 @@ class LDPSerializer(HyperlinkedModelSerializer):
                 else:
                     return super().to_internal_value(data)
 
-
         kwargs = get_nested_relation_kwargs(relation_info)
         kwargs['read_only'] = False
         kwargs['required'] = False
diff --git a/djangoldp/views.py b/djangoldp/views.py
index 8ddcbb3e..0aac9e24 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -109,8 +109,8 @@ class LDPViewSet(LDPViewSetGenerator):
         lookup_field = get_resolver().reverse_dict[model_name + '-detail'][0][0][1][0]
         meta_args = {'model': self.model, 'extra_kwargs': {
             '@id': {'lookup_field': lookup_field}},
-             'depth': self.depth,
-             'extra_fields': self.nested_fields}
+                     'depth': self.depth,
+                     'extra_fields': self.nested_fields}
         if self.fields:
             meta_args['fields'] = self.fields
         else:
-- 
GitLab