Skip to content

Commit 3937f77

Browse files
committed
Merge pull request #103 from AltSchool/feature/auto-defer-many-relations
Feature/auto defer many relations
2 parents c28f8a3 + 30f2594 commit 3937f77

File tree

9 files changed

+77
-9
lines changed

9 files changed

+77
-9
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,10 @@ DYNAMIC_REST = {
570570
# ENABLE_SERIALIZER_OPTIMIZATIONS: enable/disable representation speedups
571571
'ENABLE_SERIALIZER_OPTIMIZATIONS': True,
572572

573+
# DEFER_MANY_RELATIONS: automatically defer many-relations, unless
574+
# `deferred=False` is explicitly set on the field.
575+
'DEFER_MANY_RELATIONS': False,
576+
573577
# MAX_PAGE_SIZE: global setting for max page size.
574578
# Can be overriden at the viewset level.
575579
'MAX_PAGE_SIZE': None,

dynamic_rest/conf.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
# ENABLE_BULK_UPDATE: enable/disable update in bulk
2525
'ENABLE_BULK_UPDATE': True,
2626

27+
# DEFER_MANY_RELATIONS: automatically defer many-relations, unless
28+
# `deferred=False` is explicitly set on the field.
29+
'DEFER_MANY_RELATIONS': False,
30+
2731
# MAX_PAGE_SIZE: global setting for max page size.
2832
# Can be overriden at the viewset level.
2933
'MAX_PAGE_SIZE': None,

dynamic_rest/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
REPO_NAME = "dynamic-rest"
66
PROJECT_NAME = "Dynamic REST"
77
ORG_NAME = "AltSchool"
8-
VERSION = "1.4.5"
8+
VERSION = "1.4.6"

dynamic_rest/fields/fields.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class DynamicField(fields.Field):
2424
def __init__(
2525
self,
2626
requires=None,
27-
deferred=False,
27+
deferred=None,
2828
field_type=None,
2929
immutable=False,
3030
**kwargs
@@ -103,7 +103,7 @@ def __init__(
103103
if 'link' in kwargs:
104104
self.link = kwargs.pop('link')
105105
super(DynamicRelationField, self).__init__(**kwargs)
106-
self.kwargs['many'] = many
106+
self.kwargs['many'] = self.many = many
107107

108108
def get_model(self):
109109
"""Get the child serializer's model."""

dynamic_rest/serializers.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,22 @@ def _get_flagged_field_names(self, fields, attr, meta_attr=None):
322322
meta_list
323323
}
324324

325+
def _get_deferred_field_names(self, fields):
326+
deferred_fields = self._get_flagged_field_names(
327+
fields,
328+
'deferred'
329+
)
330+
if settings.DEFER_MANY_RELATIONS:
331+
# Auto-defer all fields, unless the 'deferred' attribute
332+
# on the field is specifically set to False.
333+
many_fields = self._get_flagged_field_names(fields, 'many')
334+
deferred_fields.update({
335+
name for name in many_fields
336+
if getattr(fields[name], 'deferred', None) is not False
337+
})
338+
339+
return deferred_fields
340+
325341
def flag_fields(self, all_fields, fields_to_flag, attr, value):
326342
for name in fields_to_flag:
327343
field = all_fields.get(name)
@@ -344,7 +360,7 @@ def get_fields(self):
344360

345361
serializer_fields = copy.deepcopy(all_fields)
346362
request_fields = self.request_fields
347-
deferred = self._get_flagged_field_names(serializer_fields, 'deferred')
363+
deferred = self._get_deferred_field_names(serializer_fields)
348364

349365
# apply request overrides
350366
if request_fields:

tests/serializers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ class Meta:
7777
fields = ('id', 'name', 'code', 'users', 'groups')
7878
deferred_fields = ('code',)
7979

80-
users = DynamicRelationField('UserSerializer', many=True, deferred=True)
81-
groups = DynamicRelationField('GroupSerializer', many=True, deferred=True)
80+
users = DynamicRelationField('UserSerializer', many=True, deferred=False)
81+
groups = DynamicRelationField('GroupSerializer', many=True)
8282

8383

8484
class GroupSerializer(DynamicModelSerializer):

tests/test_api.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from django.utils import six
77
from rest_framework.test import APITestCase
88

9-
from tests.models import Cat, Group, Location, Profile, User
10-
from tests.serializers import NestedEphemeralSerializer
9+
from tests.models import Cat, Group, Location, Permission, Profile, User
10+
from tests.serializers import NestedEphemeralSerializer, PermissionSerializer
1111
from tests.setup import create_fixture
1212

1313
UNICODE_STRING = six.unichr(9629) # unicode heart
@@ -1109,6 +1109,33 @@ def test_meta_read_only_relation_field(self):
11091109
# a 404 since a matching Profile object isn't found.
11101110
self.assertEquals(201, response.status_code)
11111111

1112+
@override_settings(
1113+
DYNAMIC_REST={
1114+
'ENABLE_LINKS': True,
1115+
'DEFER_MANY_RELATIONS': True,
1116+
}
1117+
)
1118+
def test_auto_deferral(self):
1119+
perm = Permission.objects.create(
1120+
name='test',
1121+
code=1
1122+
)
1123+
perm.groups.add(self.fixture.groups[0])
1124+
1125+
# Check serializers
1126+
fields = PermissionSerializer().get_all_fields()
1127+
self.assertIs(fields['users'].deferred, False)
1128+
self.assertIs(fields['groups'].deferred, None)
1129+
1130+
url = '/permissions/%s/' % perm.pk
1131+
r = self.client.get(url)
1132+
data = json.loads(r.content.decode('utf-8'))
1133+
self.assertFalse('groups' in data['permission'])
1134+
1135+
# users shouldn't be deferred because `deferred=False` is
1136+
# explicitly set on the field.
1137+
self.assertTrue('users' in data['permission'])
1138+
11121139

11131140
class TestDogsAPI(APITestCase):
11141141

tests/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
router.register(r'cats', viewsets.CatViewSet)
1313
router.register_resource(viewsets.DogViewSet)
1414
router.register_resource(viewsets.HorseViewSet)
15+
router.register_resource(viewsets.PermissionViewSet)
1516
router.register(r'zebras', viewsets.ZebraViewSet) # not canonical
1617
router.register(r'user_locations', viewsets.UserLocationViewSet)
1718

tests/viewsets.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
from rest_framework import exceptions
22

33
from dynamic_rest.viewsets import DynamicModelViewSet
4-
from tests.models import Cat, Dog, Group, Horse, Location, Profile, User, Zebra
4+
from tests.models import (
5+
Cat,
6+
Dog,
7+
Group,
8+
Horse,
9+
Location,
10+
Permission,
11+
Profile,
12+
User,
13+
Zebra
14+
)
515
from tests.serializers import (
616
CatSerializer,
717
DogSerializer,
818
GroupSerializer,
919
HorseSerializer,
1020
LocationSerializer,
21+
PermissionSerializer,
1122
ProfileSerializer,
1223
UserLocationSerializer,
1324
UserSerializer,
@@ -132,3 +143,8 @@ class ZebraViewSet(DynamicModelViewSet):
132143
serializer_class = ZebraSerializer
133144
queryset = Zebra.objects.all()
134145
ordering_fields = '__all__'
146+
147+
148+
class PermissionViewSet(DynamicModelViewSet):
149+
serializer_class = PermissionSerializer
150+
queryset = Permission.objects.all()

0 commit comments

Comments
 (0)