From 94147f3d301da2c99978e7bd11e8b00468633daa Mon Sep 17 00:00:00 2001 From: Jean-Baptiste <bleme@pm.me> Date: Thu, 11 Apr 2019 12:43:59 +0200 Subject: [PATCH] update: manage simple nested fielf (with foreign key) --- djangoldp/models.py | 10 +++++-- djangoldp/serializers.py | 16 ++++++++-- djangoldp/tests/models.py | 2 +- djangoldp/tests/runner.py | 16 +++++----- djangoldp/tests/tests_save.py | 56 ++++++++++++++++++++++++----------- djangoldp/tests/tests_temp.py | 11 +++++++ djangoldp/tests/urls.py | 2 +- setup.cfg | 1 + 8 files changed, 80 insertions(+), 34 deletions(-) create mode 100644 djangoldp/tests/tests_temp.py diff --git a/djangoldp/models.py b/djangoldp/models.py index e66c1170..ecb4027a 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -1,6 +1,7 @@ from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.db import models +from django.db.models.base import ModelBase from django.urls import get_resolver from django.utils.decorators import classonlymethod from guardian.shortcuts import get_perms @@ -36,8 +37,12 @@ class Model(models.Model): return cls.__clean_path(r_id) @classonlymethod - def slug_field(cls, instance): - view_name = '{}-detail'.format(instance._meta.object_name.lower()) + def slug_field(cls, instance_or_model): + if isinstance(instance_or_model, ModelBase): + object_name = instance_or_model.__name__.lower() + else: + object_name = instance_or_model._meta.object_name.lower() + view_name = '{}-detail'.format(object_name) slug_field = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][1][0]) if slug_field.startswith('/'): slug_field = slug_field[1:] @@ -113,7 +118,6 @@ class Model(models.Model): return [{'mode': {'@type': name.split('_')[0]}} for name in permissions] - class LDPSource(models.Model): container = models.URLField() federation = models.CharField(max_length=255) diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 3b0f101f..bf364e6e 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -358,7 +358,7 @@ class LDPSerializer(HyperlinkedModelSerializer): if item is empty: return empty try: - full_item = next(filter(lambda o: item[self.url_field_name] == o[self.url_field_name], object_list)) + full_item = next(filter(lambda o: self.url_field_name in o and (item[self.url_field_name] == o[self.url_field_name]), object_list)) except StopIteration: pass if full_item is None: @@ -390,10 +390,20 @@ class LDPSerializer(HyperlinkedModelSerializer): getattr(instance, field_name).add(related) def internal_create(self, validated_data, model): - nested_fields = [] + nested_fk_fields_name = list(filter(lambda key: isinstance(validated_data[key], dict), validated_data)) - # TODO replace fk_fields_name by the instance in validated_data + for field_name in nested_fk_fields_name: + field_dict = validated_data[field_name] + field_model = getattr(model, field_name).field.rel.model + slug_field = Model.slug_field(field_model) + if slug_field in field_dict: + kwargs = {slug_field: field_dict[slug_field]} + sub_inst = field_model.objects.get(**kwargs) + else: + sub_inst = self.internal_create(field_dict, field_model ) + validated_data[field_name] = sub_inst + nested_fields = [] nested_list_fields_name = list(filter(lambda key: isinstance(validated_data[key], list), validated_data)) for field_name in nested_list_fields_name: nested_fields.append((field_name, validated_data.pop(field_name))) diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py index 9f828667..d515cc05 100644 --- a/djangoldp/tests/models.py +++ b/djangoldp/tests/models.py @@ -61,7 +61,7 @@ class Batch(Model): class Meta: serializer_fields = ['@id', 'title', 'invoice', 'tasks'] - nested_fields = ["tasks"] + nested_fields = ["tasks", 'invoice'] class Task(models.Model): diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py index dcb50a99..52ea8488 100644 --- a/djangoldp/tests/runner.py +++ b/djangoldp/tests/runner.py @@ -53,14 +53,14 @@ from django.test.runner import DiscoverRunner test_runner = DiscoverRunner(verbosity=1) failures = test_runner.run_tests([ - # 'djangoldp.tests.tests_ldp_model', - # 'djangoldp.tests.tests_save', - # 'djangoldp.tests.tests_user_permissions', - # 'djangoldp.tests.tests_anonymous_permissions', - # 'djangoldp.tests.tests_update', - # 'djangoldp.tests.tests_auto_author', - 'djangoldp.tests.tests_temp' - # 'djangoldp.tests.tests_get' + 'djangoldp.tests.tests_ldp_model', + 'djangoldp.tests.tests_save', + 'djangoldp.tests.tests_user_permissions', + 'djangoldp.tests.tests_anonymous_permissions', + 'djangoldp.tests.tests_update', + 'djangoldp.tests.tests_auto_author', + # 'djangoldp.tests.tests_temp' + 'djangoldp.tests.tests_get' ]) if failures: sys.exit(failures) diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py index a0926a2c..3b63d44a 100644 --- a/djangoldp/tests/tests_save.py +++ b/djangoldp/tests/tests_save.py @@ -1,5 +1,7 @@ from django.test import TestCase +from rest_framework.utils import json +from djangoldp.models import Model from djangoldp.serializers import LDPSerializer from djangoldp.tests.models import Skill, JobOffer, Invoice, Message @@ -146,24 +148,42 @@ class Save(TestCase): self.assertIs(result.joboffer_set.get().skills.count(), 1) def test_save_fk_graph_with_nested(self): - skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire") - skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire") - - message = {"@graph": [ - {"text": "message test", - "thread": {"@id": "_.123"} - }, - {"@id": "_.123", "description": "thread"}, - ]} + post = { + '@graph': [ + { + 'http://happy-dev.fr/owl/#title': "title", + 'http://happy-dev.fr/owl/#invoice': { + '@id': "_.123" + } + }, + { + '@id': "_.123", + 'http://happy-dev.fr/owl/#title': "title 2" + } + ] + } - meta_args = {'model': Message, 'depth': 2, 'fields': ("@id", "text", "thread")} + response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json') + self.assertEqual(response.status_code, 201) + self.assertNotIn('author', response.data) + self.assertEquals(response.data['title'], "title") + self.assertEquals(response.data['invoice']['title'], "title 2") - meta_class = type('Meta', (), meta_args) - serializer_class = type(LDPSerializer)('MessageSerializer', (LDPSerializer,), {'Meta': meta_class}) - serializer = serializer_class(data=message) - serializer.is_valid() - result = serializer.save() + def test_save_fk_graph_with_existing_nested(self): + invoice = Invoice.objects.create(title="title 3") + post = { + '@graph': [ + { + 'http://happy-dev.fr/owl/#title': "title", + 'http://happy-dev.fr/owl/#invoice': { + '@id': "https://happy-dev.fr{}{}/".format(Model.container_id(invoice), invoice.id) + } + } + ] + } - self.assertEquals(result.text, "message test") - self.assertIsNotNone(result.thread) - self.assertEquals(result.thread.description, "thread") + response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json') + self.assertEqual(response.status_code, 201) + self.assertNotIn('author', response.data) + self.assertEquals(response.data['title'], "title") + self.assertEquals(response.data['invoice']['title'], "title 3") diff --git a/djangoldp/tests/tests_temp.py b/djangoldp/tests/tests_temp.py new file mode 100644 index 00000000..309931fd --- /dev/null +++ b/djangoldp/tests/tests_temp.py @@ -0,0 +1,11 @@ +import json + +from rest_framework.test import APITestCase + +from djangoldp.models import Model +from djangoldp.tests.models import Invoice + + +class TestTemp(APITestCase): + pass + diff --git a/djangoldp/tests/urls.py b/djangoldp/tests/urls.py index 7ac1c38f..a686bd1b 100644 --- a/djangoldp/tests/urls.py +++ b/djangoldp/tests/urls.py @@ -5,7 +5,7 @@ from djangoldp.tests.models import Skill, JobOffer, Message, Thread, Dummy from djangoldp.views import LDPViewSet urlpatterns = [ - url(r'^messages/', LDPViewSet.urls(model=Message, permission_classes=[], fields=["@id", "text"], nested_fields=[])), + url(r'^messages/', LDPViewSet.urls(model=Message, permission_classes=[], fields=["@id", "text", "thread"], nested_fields=['thread'])), url(r'^threads/', LDPViewSet.urls(model=Thread, nested_fields=["message_set"], permission_classes=())), url(r'^users/', LDPViewSet.urls(model=settings.AUTH_USER_MODEL, permission_classes=[])), url(r'^dummys/', LDPViewSet.urls(model=Dummy, permission_classes=[], lookup_field='slug',)), diff --git a/setup.cfg b/setup.cfg index 70a69cd1..bc9625e0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,6 +14,7 @@ setup_requires = install_requires = django~=1.11 django_rest_framework + requests pyld django-guardian -- GitLab