Skip to content

Commit 6c5494b

Browse files
committed
Added support for meta objects in serializers and list serializers
1 parent 4bfed36 commit 6c5494b

File tree

4 files changed

+74
-5
lines changed

4 files changed

+74
-5
lines changed

example/serializers.py

+12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
from datetime import datetime
12
from rest_framework_json_api import serializers, relations
23
from example.models import Blog, Entry, Author, Comment
34

45

56
class BlogSerializer(serializers.ModelSerializer):
67

8+
copyright = serializers.SerializerMethodField()
9+
10+
def get_copyright(self, obj):
11+
return datetime.now().year
12+
713
class Meta:
814
model = Blog
915
fields = ('name', )
16+
meta_fields = ('copyright',)
1017

1118

1219
class EntrySerializer(serializers.ModelSerializer):
@@ -24,6 +31,7 @@ def __init__(self, *args, **kwargs):
2431
'suggested': 'example.serializers.EntrySerializer',
2532
}
2633

34+
body_format = serializers.SerializerMethodField()
2735
comments = relations.ResourceRelatedField(
2836
source='comment_set', many=True, read_only=True)
2937
suggested = relations.SerializerMethodResourceRelatedField(
@@ -32,10 +40,14 @@ def __init__(self, *args, **kwargs):
3240
def get_suggested(self, obj):
3341
return Entry.objects.exclude(pk=obj.pk).first()
3442

43+
def get_body_format(self, obj):
44+
return 'text'
45+
3546
class Meta:
3647
model = Entry
3748
fields = ('blog', 'headline', 'body_text', 'pub_date', 'mod_date',
3849
'authors', 'comments', 'suggested',)
50+
meta_fields = ('body_format',)
3951

4052

4153
class AuthorSerializer(serializers.ModelSerializer):

rest_framework_json_api/renderers.py

+28-3
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,20 @@ def extract_included(fields, resource, resource_instance, included_resources):
319319

320320
return utils.format_keys(included_data)
321321

322+
@staticmethod
323+
def extract_meta(serializer, resource):
324+
if hasattr(serializer, 'child'):
325+
meta = getattr(serializer.child, 'Meta', None)
326+
else:
327+
meta = getattr(serializer, 'Meta', None)
328+
meta_fields = getattr(meta, 'meta_fields', {})
329+
data = OrderedDict()
330+
for field_name in meta_fields:
331+
data.update({
332+
field_name: resource.get(field_name)
333+
})
334+
return data
335+
322336
@staticmethod
323337
def build_json_resource_obj(fields, resource, resource_instance, resource_name):
324338
resource_data = [
@@ -409,8 +423,13 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
409423
for position in range(len(serializer_data)):
410424
resource = serializer_data[position] # Get current resource
411425
resource_instance = resource_serializer.instance[position] # Get current instance
412-
json_api_data.append(
413-
self.build_json_resource_obj(fields, resource, resource_instance, resource_name))
426+
427+
json_resource_obj = self.build_json_resource_obj(fields, resource, resource_instance, resource_name)
428+
meta = self.extract_meta(resource_serializer, resource)
429+
if meta:
430+
json_resource_obj.update({'meta': utils.format_keys(meta)})
431+
json_api_data.append(json_resource_obj)
432+
414433
included = self.extract_included(fields, resource, resource_instance, included_resources)
415434
if included:
416435
json_api_included.extend(included)
@@ -420,6 +439,11 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
420439
fields = utils.get_serializer_fields(data.serializer)
421440
resource_instance = data.serializer.instance
422441
json_api_data = self.build_json_resource_obj(fields, data, resource_instance, resource_name)
442+
443+
meta = self.extract_meta(data.serializer, data)
444+
if meta:
445+
json_api_data.update({'meta': utils.format_keys(meta)})
446+
423447
included = self.extract_included(fields, data, resource_instance, included_resources)
424448
if included:
425449
json_api_included.extend(included)
@@ -453,7 +477,8 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
453477
render_data['included'] = sorted(unique_compound_documents, key=lambda item: (item['type'], item['id']))
454478

455479
if isinstance(data, dict) and data.get('meta'):
456-
render_data['meta'] = data.get('meta')
480+
# add top level meta (such as pagination data)
481+
render_data.update({'meta': utils.format_keys(data.get('meta'))})
457482

458483
return super(JSONRenderer, self).render(
459484
render_data, accepted_media_type, renderer_context

rest_framework_json_api/serializers.py

+21
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,24 @@ class ModelSerializer(IncludedResourcesValidationMixin, SparseFieldsetsMixin, Mo
136136
* A mixin class to enable validation of included resources is included
137137
"""
138138
serializer_related_field = ResourceRelatedField
139+
140+
def __init__(self, *args, **kwargs):
141+
meta_fields = getattr(self.Meta, 'meta_fields', [])
142+
# we add meta_fields to fields so they will be serialized like usual
143+
self.Meta.fields = tuple(tuple(self.Meta.fields) + tuple(meta_fields))
144+
super(ModelSerializer, self).__init__(*args, **kwargs)
145+
146+
def get_field_names(self, declared_fields, info):
147+
"""
148+
We override the parent to omit explicity defined meta fields (such
149+
as SerializerMethodFields) from the list of declared fields
150+
"""
151+
meta_fields = getattr(self.Meta, 'meta_fields', None)
152+
153+
declared = OrderedDict()
154+
for field_name in declared_fields.keys():
155+
field = declared_fields[field_name]
156+
if field_name not in meta_fields:
157+
declared[field_name] = field
158+
return super(ModelSerializer, self).get_field_names(declared, info)
159+

rest_framework_json_api/utils.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,22 @@ def get_resource_name(context):
6565

6666

6767
def get_serializer_fields(serializer):
68+
fields = None
6869
if hasattr(serializer, 'child'):
69-
return getattr(serializer.child, 'fields')
70+
fields = getattr(serializer.child, 'fields')
71+
meta = getattr(serializer.child, 'Meta', None)
7072
if hasattr(serializer, 'fields'):
71-
return getattr(serializer, 'fields')
73+
fields = getattr(serializer, 'fields')
74+
meta = getattr(serializer, 'Meta', None)
7275

76+
if fields:
77+
meta_fields = getattr(meta, 'meta_fields', {})
78+
for field in meta_fields:
79+
try:
80+
fields.pop(field)
81+
except KeyError:
82+
pass
83+
return fields
7384

7485
def format_keys(obj, format_type=None):
7586
"""

0 commit comments

Comments
 (0)