forked from django-json-api/django-rest-framework-json-api
- Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmetadata.py
144 lines (125 loc) · 5.52 KB
/
metadata.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
fromcollectionsimportOrderedDict
fromdjango.db.models.fieldsimportrelated
fromdjango.utils.encodingimportforce_text
fromrest_frameworkimportserializers
fromrest_framework.metadataimportSimpleMetadata
fromrest_framework.settingsimportapi_settings
fromrest_framework.utils.field_mappingimportClassLookupDict
fromrest_framework_json_api.utilsimportget_related_resource_type
classJSONAPIMetadata(SimpleMetadata):
"""
This is the JSON:API metadata implementation.
It returns an ad-hoc set of information about the view.
There are not any formalized standards for `OPTIONS` responses
for us to base this on.
"""
type_lookup=ClassLookupDict({
serializers.Field: 'GenericField',
serializers.RelatedField: 'Relationship',
serializers.BooleanField: 'Boolean',
serializers.NullBooleanField: 'Boolean',
serializers.CharField: 'String',
serializers.URLField: 'URL',
serializers.EmailField: 'Email',
serializers.RegexField: 'Regex',
serializers.SlugField: 'Slug',
serializers.IntegerField: 'Integer',
serializers.FloatField: 'Float',
serializers.DecimalField: 'Decimal',
serializers.DateField: 'Date',
serializers.DateTimeField: 'DateTime',
serializers.TimeField: 'Time',
serializers.ChoiceField: 'Choice',
serializers.MultipleChoiceField: 'MultipleChoice',
serializers.FileField: 'File',
serializers.ImageField: 'Image',
serializers.ListField: 'List',
serializers.DictField: 'Dict',
serializers.Serializer: 'Serializer',
})
try:
relation_type_lookup=ClassLookupDict({
related.ManyToManyDescriptor: 'ManyToMany',
related.ReverseManyToOneDescriptor: 'OneToMany',
related.ForwardManyToOneDescriptor: 'ManyToOne',
})
exceptAttributeError:
relation_type_lookup=ClassLookupDict({
related.ManyRelatedObjectsDescriptor: 'ManyToMany',
related.ReverseManyRelatedObjectsDescriptor: 'ManyToMany',
related.ForeignRelatedObjectsDescriptor: 'OneToMany',
related.ReverseSingleRelatedObjectDescriptor: 'ManyToOne',
})
defdetermine_metadata(self, request, view):
metadata=OrderedDict()
metadata['name'] =view.get_view_name()
metadata['description'] =view.get_view_description()
metadata['renders'] = [renderer.media_typeforrendererinview.renderer_classes]
metadata['parses'] = [parser.media_typeforparserinview.parser_classes]
metadata['allowed_methods'] =view.allowed_methods
ifhasattr(view, 'get_serializer'):
actions=self.determine_actions(request, view)
ifactions:
metadata['actions'] =actions
returnmetadata
defget_serializer_info(self, serializer):
"""
Given an instance of a serializer, return a dictionary of metadata
about its fields.
"""
ifhasattr(serializer, 'child'):
# If this is a `ListSerializer` then we want to examine the
# underlying child serializer instance instead.
serializer=serializer.child
# Remove the URL field if present
serializer.fields.pop(api_settings.URL_FIELD_NAME, None)
returnOrderedDict(
[(field_name, self.get_field_info(field)) forfield_name, fieldinserializer.fields.items()]
)
defget_field_info(self, field):
"""
Given an instance of a serializer field, return a dictionary
of metadata about it.
"""
field_info=OrderedDict()
serializer=field.parent
ifisinstance(field, serializers.ManyRelatedField):
field_info['type'] =self.type_lookup[field.child_relation]
else:
field_info['type'] =self.type_lookup[field]
try:
serializer_model=getattr(serializer.Meta, 'model')
field_info['relationship_type'] =self.relation_type_lookup[getattr(serializer_model, field.field_name)]
exceptKeyError:
pass
exceptAttributeError:
pass
else:
field_info['relationship_resource'] =get_related_resource_type(field)
field_info['required'] =getattr(field, 'required', False)
attrs= [
'read_only', 'write_only', 'label', 'help_text',
'min_length', 'max_length',
'min_value', 'max_value', 'initial'
]
forattrinattrs:
value=getattr(field, attr, None)
ifvalueisnotNoneandvalue!='':
field_info[attr] =force_text(value, strings_only=True)
ifgetattr(field, 'child', None):
field_info['child'] =self.get_field_info(field.child)
elifgetattr(field, 'fields', None):
field_info['children'] =self.get_serializer_info(field)
if (notfield_info.get('read_only')
andhasattr(field, 'choices')
andnotfield_info.get('relationship_resource')):
field_info['choices'] = [
{
'value': choice_value,
'display_name': force_text(choice_name, strings_only=True)
}
forchoice_value, choice_nameinfield.choices.items()
]
ifhasattr(serializer, 'included_serializers') and'relationship_resource'infield_info:
field_info['allows_include'] =field.field_nameinserializer.included_serializers
returnfield_info