From 3fd6b5018c599c95327119ef5454a6365d177790 Mon Sep 17 00:00:00 2001
From: Calum Mackervoy <c.mackervoy@gmail.com>
Date: Wed, 15 Jul 2020 14:31:49 +0000
Subject: [PATCH] feature: serializer_fields_exclude

---
 README.md                         | 18 ++++++++++++++++++
 djangoldp/__init__.py             |  4 ++--
 djangoldp/tests/djangoldp_urls.py |  3 ++-
 djangoldp/tests/models.py         |  2 ++
 djangoldp/tests/tests_get.py      | 17 +++++++++++++++--
 djangoldp/views.py                |  2 +-
 6 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 6d3c4299..94f9d291 100644
--- a/README.md
+++ b/README.md
@@ -411,6 +411,24 @@ class Todo(Model):
 
 Only `name` will be serialized
 
+### serializer_fields_exclude
+
+```python
+from djangoldp.models import Model
+
+class Todo(Model):
+    name = models.CharField(max_length=255)
+    deadline = models.DateTimeField()
+
+    class Meta:
+        serializer_fields_exclude =  ['name']
+
+```
+
+Only `deadline` will be serialized
+
+This is achieved when `LDPViewSet` sets the `exclude` property on the serializer in `build_serializer` method. Note that if you use a custom viewset which does not extend LDPSerializer then you will need to set this property yourself
+
 ### nested_fields -- DEPRECIATED
 
 Set on a model to auto-generate viewsets and containers for nested relations (e.g. `/circles/<pk>/members/`)
diff --git a/djangoldp/__init__.py b/djangoldp/__init__.py
index ac5b5247..cf33475a 100644
--- a/djangoldp/__init__.py
+++ b/djangoldp/__init__.py
@@ -3,6 +3,6 @@ from django.db.models import options
 __version__ = '0.0.0'
 options.DEFAULT_NAMES += (
     'lookup_field', 'rdf_type', 'rdf_context', 'auto_author', 'auto_author_field', 'owner_field', 'view_set',
-    'container_path', 'permission_classes', 'serializer_fields', 'nested_fields', 'nested_fields_exclude', 'depth',
-    'anonymous_perms', 'authenticated_perms', 'owner_perms')
+    'container_path', 'permission_classes', 'serializer_fields', 'serializer_fields_exclude', 'nested_fields',
+    'nested_fields_exclude', 'depth', 'anonymous_perms', 'authenticated_perms', 'owner_perms')
 default_app_config = 'djangoldp.apps.DjangoldpConfig'
diff --git a/djangoldp/tests/djangoldp_urls.py b/djangoldp/tests/djangoldp_urls.py
index c13aad61..4766fedf 100644
--- a/djangoldp/tests/djangoldp_urls.py
+++ b/djangoldp/tests/djangoldp_urls.py
@@ -2,13 +2,14 @@ from django.conf import settings
 from django.conf.urls import url, include
 
 from djangoldp.permissions import LDPPermissions
-from djangoldp.tests.models import Skill, JobOffer, Message, Conversation, Dummy, PermissionlessDummy, Task
+from djangoldp.tests.models import Skill, JobOffer, Message, Conversation, Dummy, PermissionlessDummy, Task, DateModel
 from djangoldp.views import LDPViewSet
 
 urlpatterns = [
     url(r'^messages/', LDPViewSet.urls(model=Message, permission_classes=[LDPPermissions], fields=["@id", "text", "conversation"], nested_fields=['conversation'])),
     url(r'^conversations/', LDPViewSet.urls(model=Conversation, nested_fields=["message_set"], permission_classes=[LDPPermissions])),
     url(r'^tasks/', LDPViewSet.urls(model=Task, permission_classes=[LDPPermissions])),
+    url(r'^dates/', LDPViewSet.urls(model=DateModel, permission_classes=[LDPPermissions])),
     url(r'^dummys/', LDPViewSet.urls(model=Dummy, permission_classes=[LDPPermissions], lookup_field='slug',)),
     url(r'^permissionless-dummys/', LDPViewSet.urls(model=PermissionlessDummy, permission_classes=[LDPPermissions], lookup_field='slug',)),
 ]
diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py
index 9a3acce5..6c2b5b84 100644
--- a/djangoldp/tests/models.py
+++ b/djangoldp/tests/models.py
@@ -223,10 +223,12 @@ class Project(Model):
 
 
 class DateModel(Model):
+    excluded = models.CharField(max_length=255, null=True, default='test')
     value = models.DateField()
 
     class Meta(Model.Meta):
         rdf_type = "hd:date"
+        serializer_fields_exclude = ['excluded']
 
 
 class DateChild(Model):
diff --git a/djangoldp/tests/tests_get.py b/djangoldp/tests/tests_get.py
index adda7c7b..a32a80bd 100644
--- a/djangoldp/tests/tests_get.py
+++ b/djangoldp/tests/tests_get.py
@@ -1,8 +1,8 @@
 from rest_framework.test import APIRequestFactory, APIClient, APITestCase
-
+from datetime import datetime
 from rest_framework.test import APIRequestFactory, APIClient, APITestCase
 
-from djangoldp.tests.models import Post, Invoice, JobOffer, Skill, Batch
+from djangoldp.tests.models import Post, Invoice, JobOffer, Skill, Batch, DateModel
 
 
 class TestGET(APITestCase):
@@ -88,3 +88,16 @@ class TestGET(APITestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEquals(response.data['@id'], 'http://happy-dev.fr/invoices/{}/batches/'.format(invoice.pk))
         self.assertEquals(len(response.data['ldp:contains']), 2)
+
+    def test_serializer_excludes(self):
+        date = DateModel.objects.create(excluded='test', value=datetime.now())
+        response = self.client.get('/dates/{}/'.format(date.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertNotIn('excluded', response.data.keys())
+
+    def test_serializer_excludes_serializer_fields_set_also(self):
+        setattr(DateModel._meta, 'serializer_fields', ['value', 'excluded'])
+        date = DateModel.objects.create(excluded='test', value=datetime.now())
+        response = self.client.get('/dates/{}/'.format(date.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertNotIn('excluded', response.data.keys())
diff --git a/djangoldp/views.py b/djangoldp/views.py
index 6a2dfb8b..7c0e7df9 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -368,7 +368,7 @@ class LDPViewSet(LDPViewSetGenerator):
         if self.fields:
             meta_args['fields'] = self.fields
         else:
-            meta_args['exclude'] = self.exclude or ()
+            meta_args['exclude'] = self.exclude or Model.get_meta(self.model, 'serializer_fields_exclude') or ()
         meta_class = type('Meta', (), meta_args)
 
         from djangoldp.serializers import LDPSerializer
-- 
GitLab