diff options
| author | Tom Christie | 2014-09-24 14:09:49 +0100 |
|---|---|---|
| committer | Tom Christie | 2014-09-24 14:09:49 +0100 |
| commit | f4b1dcb167be0bbdaae2cc2a92f651536896dc16 (patch) | |
| tree | 855df19d2a09da4329274db4bc56d89b342df036 /rest_framework/metadata.py | |
| parent | aa84432f9b40849fb677d9fed803098fd392f881 (diff) | |
| download | django-rest-framework-f4b1dcb167be0bbdaae2cc2a92f651536896dc16.tar.bz2 | |
OPTIONS support
Diffstat (limited to 'rest_framework/metadata.py')
| -rw-r--r-- | rest_framework/metadata.py | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py new file mode 100644 index 00000000..580259de --- /dev/null +++ b/rest_framework/metadata.py @@ -0,0 +1,126 @@ +""" +The metadata API is used to allow cusomization of how `OPTIONS` requests +are handled. We currently provide a single default implementation that returns +some fairly ad-hoc information about the view. + +Future implementations might use JSON schema or other definations in order +to return this information in a more standardized way. +""" +from __future__ import unicode_literals + +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.utils import six +from django.utils.datastructures import SortedDict +from rest_framework import exceptions, serializers +from rest_framework.compat import force_text +from rest_framework.request import clone_request +from rest_framework.utils.field_mapping import ClassLookupDict + + +class BaseMetadata(object): + def determine_metadata(self, request, view): + """ + Return a dictionary of metadata about the view. + Used to return responses for OPTIONS requests. + """ + raise NotImplementedError(".determine_metadata() must be overridden.") + + +class SimpleMetadata(BaseMetadata): + """ + This is the default 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. + """ + label_lookup = ClassLookupDict({ + serializers.Field: 'field', + serializers.BooleanField: '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: 'multiple choice', + serializers.FileField: 'file upload', + serializers.ImageField: 'image upload', + }) + + def determine_metadata(self, request, view): + metadata = SortedDict() + metadata['name'] = view.get_view_name() + metadata['description'] = view.get_view_description() + metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes] + metadata['parses'] = [parser.media_type for parser in view.parser_classes] + if hasattr(view, 'get_serializer'): + actions = self.determine_actions(request, view) + if actions: + metadata['actions'] = actions + return metadata + + def determine_actions(self, request, view): + """ + For generic class based views we return information about + the fields that are accepted for 'PUT' and 'POST' methods. + """ + actions = {} + for method in set(['PUT', 'POST']) & set(view.allowed_methods): + view.request = clone_request(request, method) + try: + # Test global permissions + if hasattr(view, 'check_permissions'): + view.check_permissions(view.request) + # Test object permissions + if method == 'PUT' and hasattr(view, 'get_object'): + view.get_object() + except (exceptions.APIException, PermissionDenied, Http404): + pass + else: + # If user has appropriate permissions for the view, include + # appropriate metadata about the fields that should be supplied. + serializer = view.get_serializer() + actions[method] = self.get_serializer_info(serializer) + finally: + view.request = request + + return actions + + def get_serializer_info(self, serializer): + """ + Given an instance of a serializer, return a dictionary of metadata + about its fields. + """ + return SortedDict([ + (field_name, self.get_field_info(field)) + for field_name, field in six.iteritems(serializer.fields) + ]) + + def get_field_info(self, field): + """ + Given an instance of a serializer field, return a dictionary + of metadata about it. + """ + field_info = SortedDict() + field_info['type'] = self.label_lookup[field] + field_info['required'] = getattr(field, 'required', False) + + for attr in ['read_only', 'label', 'help_text', 'min_length', 'max_length']: + value = getattr(field, attr, None) + if value is not None and value != '': + field_info[attr] = force_text(value, strings_only=True) + + if hasattr(field, 'choices'): + field_info['choices'] = [ + {'value': choice_value, 'display_name': choice_name} + for choice_value, choice_name in field.choices.items() + ] + + return field_info |
