From 5ce49f469e1a8345f37851463ac292f63382f4ca Mon Sep 17 00:00:00 2001 From: Jean-Baptiste <bleme@pm.me> Date: Sat, 27 Jul 2019 13:57:22 +0200 Subject: [PATCH 1/2] update: split serializer (read / write) --- djangoldp/__init__.py | 2 +- djangoldp/models.py | 1 - djangoldp/serializers.py | 5 ++- djangoldp/tests/runner.py | 2 +- djangoldp/tests/tests_temp.py | 1 + djangoldp/tests/tests_update.py | 11 +++--- djangoldp/views.py | 59 ++++++++++++++++++++++++++++++--- 7 files changed, 65 insertions(+), 16 deletions(-) diff --git a/djangoldp/__init__.py b/djangoldp/__init__.py index 7deb3ab1..0416a0be 100644 --- a/djangoldp/__init__.py +++ b/djangoldp/__init__.py @@ -1,4 +1,4 @@ from django.db.models import options __version__ = '0.0.0' -options.DEFAULT_NAMES += ('lookup_field', 'rdf_type', 'rdf_context', 'auto_author', 'view_set', 'container_path', 'permission_classes', 'serializer_fields', 'nested_fields', 'depth', 'many_depth') \ No newline at end of file +options.DEFAULT_NAMES += ('lookup_field', 'rdf_type', 'rdf_context', 'auto_author', 'view_set', 'container_path', 'permission_classes', 'serializer_fields', 'nested_fields', 'depth') diff --git a/djangoldp/models.py b/djangoldp/models.py index a3fe37c6..7ab277f0 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -66,7 +66,6 @@ class Model(models.Model): default_permissions = ('add', 'change', 'delete', 'view', 'control') abstract = True depth = 1 - many_depth = 0 @classonlymethod def resolve_id(cls, id): diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 91972b0b..4f806051 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -258,8 +258,7 @@ class LDPSerializer(HyperlinkedModelSerializer): nested_fields=Model.get_meta(model_class, 'nested_fields', [])) parent_depth = max(getattr(self.parent.Meta, "depth", 0) - 1, 0) serializer_generator.depth = parent_depth - serializer_generator.many_depth = max(getattr(self.parent.Meta, "many_depth", 0) - 1, 0) - serializer = serializer_generator.build_serializer()(context=self.parent.context) + serializer = serializer_generator.build_read_serializer()(context=self.parent.context) if parent_depth is 0: serializer.Meta.fields = ["@id"] return {'@id': '{}{}{}/'.format(settings.SITE_URL, '{}{}/', self.source), @@ -405,7 +404,7 @@ class LDPSerializer(HyperlinkedModelSerializer): def many_init(cls, *args, **kwargs): kwargs['child'] = cls(**kwargs) try: - cls.Meta.depth = cls.compute_depth(kwargs['context']['view'].many_depth, cls.Meta.model, 'many_depth') + cls.Meta.depth = cls.compute_depth(kwargs['context']['view'].depth, cls.Meta.model, 'depth') except KeyError: pass return ContainerSerializer(*args, **kwargs) diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py index 296716a1..230740f0 100644 --- a/djangoldp/tests/runner.py +++ b/djangoldp/tests/runner.py @@ -63,7 +63,7 @@ failures = test_runner.run_tests([ 'djangoldp.tests.tests_auto_author', 'djangoldp.tests.tests_get', 'djangoldp.tests.tests_delete', - # 'djangoldp.tests.tests_temp' + 'djangoldp.tests.tests_temp' ]) if failures: diff --git a/djangoldp/tests/tests_temp.py b/djangoldp/tests/tests_temp.py index 1864f386..e7a80ca5 100644 --- a/djangoldp/tests/tests_temp.py +++ b/djangoldp/tests/tests_temp.py @@ -18,3 +18,4 @@ class TestTemp(TestCase): pass + diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py index c1458988..292b0baf 100644 --- a/djangoldp/tests/tests_update.py +++ b/djangoldp/tests/tests_update.py @@ -281,8 +281,8 @@ class Update(TestCase): { '@id': "_:b975", 'http://happy-dev.fr/owl/#description': "user description", - 'http://happy-dev.fr/owl/#dummy': { - '@id' : './' + 'http://happy-dev.fr/owl/#dummy': { + '@id': './' } }, { @@ -366,7 +366,8 @@ class Update(TestCase): def test_missing_field_should_not_be_removed_with_fk_relation(self): user = User.objects.create(username="alex", password="test") peer = User.objects.create(username="sylvain", password="test2") - conversation = Conversation.objects.create(author_user=user, peer_user=peer, description="conversation description") + conversation = Conversation.objects.create(author_user=user, peer_user=peer, + description="conversation description") body = [ { '@id': "/conversations/{}/".format(conversation.pk), @@ -381,7 +382,8 @@ class Update(TestCase): def test_empty_field_should_be_removed_with_fk_relation(self): user = User.objects.create(username="alex", password="test") peer = User.objects.create(username="sylvain", password="test2") - conversation = Conversation.objects.create(author_user=user, peer_user=peer, description="conversation description") + conversation = Conversation.objects.create(author_user=user, peer_user=peer, + description="conversation description") body = [ { '@id': "/conversations/{}/".format(conversation.pk), @@ -393,4 +395,3 @@ class Update(TestCase): content_type='application/ld+json') self.assertEqual(response.status_code, 200) self.assertEqual(response.data['peer_user'], None) - diff --git a/djangoldp/views.py b/djangoldp/views.py index b20cd12c..a6e342c2 100644 --- a/djangoldp/views.py +++ b/djangoldp/views.py @@ -9,10 +9,13 @@ from django.shortcuts import get_object_or_404 from django.utils.decorators import classonlymethod from guardian.shortcuts import get_objects_for_user from pyld import jsonld +from rest_framework import status from rest_framework.authentication import SessionAuthentication from rest_framework.parsers import JSONParser from rest_framework.renderers import JSONRenderer from rest_framework.viewsets import ModelViewSet +from rest_framework.response import Response + from djangoldp.models import LDPSource, Model @@ -90,8 +93,7 @@ class LDPViewSet(LDPViewSetGenerator): """An automatically generated viewset that serves models following the Linked Data Platform convention""" fields = None exclude = None - depth = 2 - many_depth = 1 + depth = 1 renderer_classes = (JSONLDRenderer,) parser_classes = (JSONLDParser,) authentication_classes = (NoCSRFAuthentication,) @@ -103,22 +105,69 @@ class LDPViewSet(LDPViewSetGenerator): if hasattr(p, 'filter_class') and p.filter_class: self.filter_backends = p.filter_class - self.serializer_class = self.build_serializer() + self.serializer_class = self.build_read_serializer() + self.write_serializer_class = self.build_write_serializer() - def build_serializer(self): + def build_read_serializer(self): model_name = self.model._meta.object_name.lower() 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} + return self.build_serializer(meta_args, 'Read') + + def build_write_serializer(self): + model_name = self.model._meta.object_name.lower() + 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': 10, + 'extra_fields': self.nested_fields} + return self.build_serializer(meta_args, 'Write') + + def build_serializer(self, meta_args, name_prefix): if self.fields: meta_args['fields'] = self.fields else: meta_args['exclude'] = self.exclude or () meta_class = type('Meta', (), meta_args) from djangoldp.serializers import LDPSerializer - return type(LDPSerializer)(model_name + 'Serializer', (LDPSerializer,), {'Meta': meta_class}) + return type(LDPSerializer)(self.model._meta.object_name.lower() + name_prefix + 'Serializer', (LDPSerializer,), {'Meta': meta_class}) + + def create(self, request, *args, **kwargs): + serializer = self.get_write_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + def get_write_serializer(self, *args, **kwargs): + """ + Return the serializer instance that should be used for validating and + deserializing input, and for serializing output. + """ + serializer_class = self.get_write_serializer_class() + kwargs['context'] = self.get_serializer_context() + return serializer_class(*args, **kwargs) + + def get_write_serializer_class(self): + """ + Return the class to use for the serializer. + Defaults to using `self.write_serializer_class`. + + You may want to override this if you need to provide different + serializations depending on the incoming request. + + (Eg. admins get full serialization, others get basic serialization) + """ + assert self.write_serializer_class is not None, ( + "'%s' should either include a `write_serializer_class` attribute, " + "or override the `get_write_serializer_class()` method." + % self.__class__.__name__ + ) + + return self.write_serializer_class def perform_create(self, serializer, **kwargs): if hasattr(self.model._meta, 'auto_author') and isinstance(self.request.user, get_user_model()): -- GitLab From 5cc8014fde7577feb5f81de3a6e05ba94f6ea52c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste <bleme@pm.me> Date: Sat, 27 Jul 2019 15:04:23 +0200 Subject: [PATCH 2/2] update: set default depth to 0 --- djangoldp/models.py | 2 +- djangoldp/serializers.py | 16 ---------------- djangoldp/tests/runner.py | 2 +- djangoldp/tests/tests_temp.py | 1 - 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/djangoldp/models.py b/djangoldp/models.py index 7ab277f0..ee4ebe72 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -65,7 +65,7 @@ class Model(models.Model): class Meta: default_permissions = ('add', 'change', 'delete', 'view', 'control') abstract = True - depth = 1 + depth = 0 @classonlymethod def resolve_id(cls, id): diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 4f806051..620516c0 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -238,8 +238,6 @@ class LDPSerializer(HyperlinkedModelSerializer): return data def build_field(self, field_name, info, model_class, nested_depth): - nested_depth = self.compute_depth(nested_depth, model_class) - return super().build_field(field_name, info, model_class, nested_depth) def build_property_field(self, field_name, model_class): @@ -318,7 +316,6 @@ class LDPSerializer(HyperlinkedModelSerializer): return type(field_class.__name__ + 'Valued', (JSonLDStandardField, field_class), {}), field_kwargs def build_nested_field(self, field_name, relation_info, nested_depth): - nested_depth = self.compute_depth(nested_depth, self.Meta.model) class NestedLDPSerializer(self.__class__): @@ -390,23 +387,10 @@ class LDPSerializer(HyperlinkedModelSerializer): kwargs['required'] = False return NestedLDPSerializer, kwargs - @classmethod - def compute_depth(cls, depth, model_class, name='depth'): - try: - model_depth = getattr(model_class._meta, 'depth', getattr(model_class.Meta, 'depth', 10)) - depth = min(depth, int(model_depth)) - except AttributeError: - depth = min(depth, int(getattr(model_class._meta, 'depth', 1))) - - return depth @classmethod def many_init(cls, *args, **kwargs): kwargs['child'] = cls(**kwargs) - try: - cls.Meta.depth = cls.compute_depth(kwargs['context']['view'].depth, cls.Meta.model, 'depth') - except KeyError: - pass return ContainerSerializer(*args, **kwargs) def get_value(self, dictionary): diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py index 230740f0..296716a1 100644 --- a/djangoldp/tests/runner.py +++ b/djangoldp/tests/runner.py @@ -63,7 +63,7 @@ failures = test_runner.run_tests([ 'djangoldp.tests.tests_auto_author', 'djangoldp.tests.tests_get', 'djangoldp.tests.tests_delete', - 'djangoldp.tests.tests_temp' + # 'djangoldp.tests.tests_temp' ]) if failures: diff --git a/djangoldp/tests/tests_temp.py b/djangoldp/tests/tests_temp.py index e7a80ca5..1864f386 100644 --- a/djangoldp/tests/tests_temp.py +++ b/djangoldp/tests/tests_temp.py @@ -18,4 +18,3 @@ class TestTemp(TestCase): pass - -- GitLab