1 3
from collections import OrderedDict
2

3
# Django
4 3
from django.core.exceptions import PermissionDenied
5 3
from django.http import Http404
6 3
from django.utils.encoding import force_text, smart_text
7

8
# Django REST Framework
9 3
from rest_framework import exceptions
10 3
from rest_framework import metadata
11 3
from rest_framework import serializers
12 3
from rest_framework.relations import RelatedField, ManyRelatedField
13 3
from rest_framework.request import clone_request
14

15

16 3
class Metadata(metadata.SimpleMetadata):
17

18 3
    def get_field_info(self, field):
19 0
        field_info = OrderedDict()
20 0
        field_info['type'] = self.label_lookup[field]
21 0
        field_info['required'] = getattr(field, 'required', False)
22

23 0
        text_attrs = [
24
            'read_only', 'label', 'help_text',
25
            'min_length', 'max_length',
26
            'min_value', 'max_value',
27
            'category', 'category_slug',
28
            'defined_in_file'
29
        ]
30

31 0
        for attr in text_attrs:
32 0
            value = getattr(field, attr, None)
33 0
            if value is not None and value != '':
34 0
                field_info[attr] = force_text(value, strings_only=True)
35

36 0
        placeholder = getattr(field, 'placeholder', serializers.empty)
37 0
        if placeholder is not serializers.empty:
38 0
            field_info['placeholder'] = placeholder
39

40
        # Update help text for common fields.
41 0
        serializer = getattr(field, 'parent', None)
42 0
        if serializer:
43 0
            field_help_text = {
44
                'id': 'Database ID for this {}.',
45
                'name': 'Name of this {}.',
46
                'description': 'Optional description of this {}.',
47
                'type': 'Data type for this {}.',
48
                'url': 'URL for this {}.',
49
                'related': 'Data structure with URLs of related resources.',
50
                'summary_fields': 'Data structure with name/description for related resources.',
51
                'created': 'Timestamp when this {} was created.',
52
                'modified': 'Timestamp when this {} was last modified.',
53
            }
54 0
            if field.field_name in field_help_text:
55 0
                if hasattr(serializer, 'Meta') and hasattr(serializer.Meta, 'model'):
56 0
                    opts = serializer.Meta.model._meta.concrete_model._meta
57 0
                    verbose_name = smart_text(opts.verbose_name)
58 0
                    field_info['help_text'] = field_help_text[field.field_name].format(verbose_name)
59

60
        # Indicate if a field has a default value.
61
        # FIXME: Still isn't showing all default values?
62 0
        try:
63 0
            default = field.get_default()
64 0
            field_info['default'] = default
65 0
        except serializers.SkipField:
66 0
            pass
67

68 0
        if getattr(field, 'child', None):
69 0
            field_info['child'] = self.get_field_info(field.child)
70 0
        elif getattr(field, 'fields', None):
71 0
            field_info['children'] = self.get_serializer_info(field)
72

73 0
        if not isinstance(field, (RelatedField, ManyRelatedField)) and hasattr(field, 'choices'):
74 0
            field_info['choices'] = [(choice_v, choice_n) for choice_v, choice_n in field.choices.items()]
75

76
        # Indicate if a field is write-only.
77 0
        if getattr(field, 'write_only', False):
78 0
            field_info['write_only'] = True
79

80
        # Update type of fields returned...
81 0
        if field.field_name == 'type':
82 0
            field_info['type'] = 'choice'
83 0
        elif field.field_name == 'url':
84 0
            field_info['type'] = 'string'
85 0
        elif field.field_name in ('related', 'summary_fields'):
86 0
            field_info['type'] = 'object'
87 0
        elif field.field_name in ('created', 'modified'):
88 0
            field_info['type'] = 'datetime'
89

90 0
        return field_info
91

92 3
    def get_serializer_info(self, serializer, method=None):
93 0
        filterer = getattr(serializer, 'filter_field_metadata', lambda fields, method: fields)
94 0
        return filterer(
95
            super(Metadata, self).get_serializer_info(serializer),
96
            method
97
        )
98

99 3
    def determine_actions(self, request, view):
100
        # Add field information for GET requests (so field names/labels are
101
        # available even when we can't POST/PUT).
102 0
        actions = {}
103 0
        for method in {'GET', 'PUT', 'POST'} & set(view.allowed_methods):
104 0
            view.request = clone_request(request, method)
105 0
            obj = None
106 0
            try:
107
                # Test global permissions
108 0
                if hasattr(view, 'check_permissions'):
109 0
                    view.check_permissions(view.request)
110
                # Test object permissions
111 0
                if method == 'PUT' and hasattr(view, 'get_object'):
112 0
                    obj = view.get_object()
113 0
            except (exceptions.APIException, PermissionDenied, Http404):
114 0
                continue
115
            else:
116
                # If user has appropriate permissions for the view, include
117
                # appropriate metadata about the fields that should be supplied.
118 0
                serializer = view.get_serializer(instance=obj)
119 0
                actions[method] = self.get_serializer_info(serializer, method=method)
120
            finally:
121 0
                view.request = request
122

123 0
            for field, meta in list(actions[method].items()):
124 0
                if not isinstance(meta, dict):
125 0
                    continue
126

127
                # Add type choices if available from the serializer.
128 0
                if field == 'type' and hasattr(serializer, 'get_type_choices'):
129 0
                    meta['choices'] = serializer.get_type_choices()
130

131
                # For GET method, remove meta attributes that aren't relevant
132
                # when reading a field and remove write-only fields.
133 0
                if method == 'GET':
134 0
                    attrs_to_remove = ('required', 'read_only', 'default', 'min_length', 'max_length', 'placeholder')
135 0
                    for attr in attrs_to_remove:
136 0
                        meta.pop(attr, None)
137 0
                        meta.get('child', {}).pop(attr, None)
138 0
                    if meta.pop('write_only', False):
139 0
                        actions['GET'].pop(field)
140

141
                # For PUT/POST methods, remove read-only fields.
142 0
                if method in ('PUT', 'POST'):
143
                    # This value should always be False for PUT/POST, so don't
144
                    # show it (file-based read-only settings can't be updated)
145 0
                    meta.pop('defined_in_file', False)
146

147 0
                    if meta.pop('read_only', False):
148 0
                        if field == 'id' and hasattr(view, 'attach'):
149 0
                            continue
150 0
                        actions[method].pop(field)
151

152 0
        return actions
153

154 3
    def determine_metadata(self, request, view):
155
        # store request on self so we can use it to generate field defaults
156
        # (such as TOWER_URL_BASE)
157 0
        self.request = request
158

159 0
        try:
160 0
            setattr(view, '_request', request)
161 0
            metadata = super(Metadata, self).determine_metadata(request, view)
162
        finally:
163 0
            delattr(view, '_request')
164

165
        # Add type(s) handled by this view/serializer.
166 0
        if hasattr(view, 'get_serializer'):
167 0
            serializer = view.get_serializer()
168 0
            if hasattr(serializer, 'get_types'):
169 0
                metadata['types'] = serializer.get_types()
170

171
        # Add search fields if available from the view.
172 0
        if getattr(view, 'search_fields', None):
173 0
            metadata['search_fields'] = view.search_fields
174

175
        # Add related search fields if available from the view.
176 0
        if getattr(view, 'related_search_fields', None):
177 0
            metadata['related_search_fields'] = view.related_search_fields
178

179 0
        from rest_framework import generics
180 0
        if isinstance(view, generics.ListAPIView) and hasattr(view, 'paginator'):
181 0
            metadata['max_page_size'] = view.paginator.max_page_size
182

183 0
        return metadata
184

185

186 3
class SublistAttachDetatchMetadata(Metadata):
187

188 3
    def determine_actions(self, request, view):
189 0
        actions = super(SublistAttachDetatchMetadata, self).determine_actions(request, view)
190 0
        method = 'POST'
191 0
        if method in actions:
192 0
            for field in actions[method]:
193 0
                if field == 'id':
194 0
                    continue
195 0
                actions[method].pop(field)
196 0
        return actions

Read our documentation on viewing source code .

Loading