diff options
| author | Nikolaus Schlemm | 2013-05-18 18:34:21 +0200 | 
|---|---|---|
| committer | Nikolaus Schlemm | 2013-05-18 18:34:21 +0200 | 
| commit | 843ae6023753a0373eb2e6398ddda93e4da74de5 (patch) | |
| tree | 4e24d5c121a226b1ce7cc160f8e8e0c5204cc1b8 /rest_framework | |
| parent | 7f1cc82f96c2ba4064b28957a8b2d5b313be3c40 (diff) | |
| parent | fecadacab150aab48b8b84f4f0e5340ead74c287 (diff) | |
| download | django-rest-framework-843ae6023753a0373eb2e6398ddda93e4da74de5.tar.bz2 | |
Merge branch 'issue-192-expose-fields-for-options' of git://github.com/grimborg/django-rest-framework into issue-192-expose-fields-for-options
Conflicts:
	rest_framework/tests/fields.py
Diffstat (limited to 'rest_framework')
| -rw-r--r-- | rest_framework/fields.py | 91 | ||||
| -rw-r--r-- | rest_framework/tests/fields.py | 78 | ||||
| -rw-r--r-- | rest_framework/views.py | 2 | 
3 files changed, 169 insertions, 2 deletions
| diff --git a/rest_framework/fields.py b/rest_framework/fields.py index fc14184c..0f07aaba 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -23,13 +23,44 @@ from django.utils.translation import ugettext_lazy as _  from django.utils.datastructures import SortedDict  from rest_framework import ISO_8601 -from rest_framework.compat import timezone, parse_date, parse_datetime, parse_time +from rest_framework.compat import (timezone, parse_date, parse_datetime, +                                   parse_time)  from rest_framework.compat import BytesIO  from rest_framework.compat import six  from rest_framework.compat import smart_text  from rest_framework.settings import api_settings +HUMANIZED_FIELD_TYPES = { +    'BooleanField': u'Boolean', +    'CharField': u'Single Character', +    'ChoiceField': u'Single Choice', +    'ComboField': u'Single Choice', +    'DateField': u'Date', +    'DateTimeField': u'Date and Time', +    'DecimalField': u'Decimal', +    'EmailField': u'Email', +    'Field': u'Field', +    'FileField': u'File', +    'FilePathField': u'File Path', +    'FloatField': u'Float', +    'GenericIPAddressField': u'Generic IP Address', +    'IPAddressField': u'IP Address', +    'ImageField': u'Image', +    'IntegerField': u'Integer', +    'MultiValueField': u'Multiple Value', +    'MultipleChoiceField': u'Multiple Choice', +    'NullBooleanField': u'Nullable Boolean', +    'RegexField': u'Regular Expression', +    'SlugField': u'Slug', +    'SplitDateTimeField': u'Split Date and Time', +    'TimeField': u'Time', +    'TypedChoiceField': u'Typed Single Choice', +    'TypedMultipleChoiceField': u'Typed Multiple Choice', +    'URLField': u'URL', +} + +  def is_simple_callable(obj):      """      True if the object is a callable that takes no arguments. @@ -62,7 +93,8 @@ def get_component(obj, attr_name):  def readable_datetime_formats(formats): -    format = ', '.join(formats).replace(ISO_8601, 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]') +    format = ', '.join(formats).replace(ISO_8601, +             'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HHMM|-HHMM|Z]')      return humanize_strptime(format) @@ -71,6 +103,61 @@ def readable_date_formats(formats):      return humanize_strptime(format) +def humanize_field_type(field_type): +    """Return a human-readable name for a field type. + +    :param field_type: Either a field type class (for example +        django.forms.fields.DateTimeField), or the name of a field type +        (for example "DateTimeField"). + +    :return: unicode + +    """ +    if isinstance(field_type, basestring): +        field_type_name = field_type +    else: +        field_type_name = field_type.__name__ +    try: +        return HUMANIZED_FIELD_TYPES[field_type_name] +    except KeyError: +        humanized = re.sub('([a-z0-9])([A-Z])', r'\1 \2', field_type_name) +        return humanized.capitalize() + + +def humanize_field(field): +    """Return a human-readable description of a field. + +    :param field: A Django field. + +    :return: A dictionary of the form {type: type name, required: bool, +             label: field label: read_only: bool, +             help_text: optional help text} + +    """ +    humanized = { +        'type': humanize_field_type(field.__class__), +        'required': getattr(field, 'required', False), +        'label': field.label, +    } +    optional_attrs = ['read_only', 'help_text'] +    for attr in optional_attrs: +        if hasattr(field, attr): +            humanized[attr] = getattr(field, attr) +    return humanized + + +def humanize_form_fields(form): +    """Return a humanized description of all the fields in a form. + +    :param form: A Django form. +    :return: A dictionary of {field_label: humanized description} + +    """ +    fields = SortedDict([(name, humanize_field(field)) +                         for name, field in form.fields.iteritems()]) +    return fields + +  def readable_time_formats(formats):      format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]')      return humanize_strptime(format) diff --git a/rest_framework/tests/fields.py b/rest_framework/tests/fields.py index dad69975..7a5ed718 100644 --- a/rest_framework/tests/fields.py +++ b/rest_framework/tests/fields.py @@ -4,6 +4,9 @@ General serializer field tests.  from __future__ import unicode_literals  from django.utils.datastructures import SortedDict  import datetime +from rest_framework.fields import (humanize_field, humanize_field_type, +                                   humanize_form_fields) +from django import forms  from decimal import Decimal  from django.db import models  from django.test import TestCase @@ -11,6 +14,9 @@ from django.core import validators  from rest_framework import serializers  from rest_framework.serializers import Serializer  from rest_framework.tests.models import RESTFrameworkModel +from rest_framework.fields import Field +from collections import namedtuple +from uuid import uuid4  class TimestampedModel(models.Model): @@ -809,3 +815,75 @@ class URLFieldTests(TestCase):          serializer = URLFieldSerializer(data={})          self.assertEqual(serializer.is_valid(), True)          self.assertEqual(getattr(serializer.fields['url_field'], 'max_length'), 20) + + +class HumanizedFieldType(TestCase): +    def test_standard_type_classes(self): +        for field_type_name in forms.fields.__all__: +            field_type = getattr(forms.fields, field_type_name) +            humanized = humanize_field_type(field_type) +            self.assert_valid_name(humanized) + +    def test_standard_type_names(self): +        for field_type_name in forms.fields.__all__: +            humanized = humanize_field_type(field_type_name) +            self.assert_valid_name(humanized) + +    def test_custom_type_name(self): +        humanized = humanize_field_type('SomeCustomType') +        self.assertEquals(humanized, u'Some custom type') + +    def test_custom_type(self): +        custom_type = namedtuple('SomeCustomType', []) +        humanized = humanize_field_type(custom_type) +        self.assertEquals(humanized, u'Some custom type') + +    def assert_valid_name(self, humanized): +        """A humanized field name is valid if it's a non-empty +        unicode. + +        """ +        self.assertIsInstance(humanized, unicode) +        self.assertTrue(humanized) + + +class HumanizedField(TestCase): +    def setUp(self): +        self.required_field = Field() +        self.required_field.label = uuid4().hex +        self.required_field.required = True + +        self.optional_field = Field() +        self.optional_field.label = uuid4().hex +        self.optional_field.required = False + +    def test_required(self): +        self.assertEqual(humanize_field(self.required_field)['required'], True) + +    def test_optional(self): +        self.assertEqual(humanize_field(self.optional_field)['required'], +                         False) + +    def test_label(self): +        for field in (self.required_field, self.optional_field): +            self.assertEqual(humanize_field(field)['label'], field.label) + + +class Form(forms.Form): +    field1 = forms.CharField(max_length=3, label='field one') +    field2 = forms.CharField(label='field two') + + +class HumanizedSerializer(TestCase): +    def setUp(self): +        self.serializer = TimestampedModelSerializer() + +    def test_humanized(self): +        humanized = humanize_form_fields(Form()) +        self.assertEqual(humanized, { +            'field1': { +                u'help_text': u'', u'required': True, +                u'type': u'Single Character', u'label': 'field one'}, +            'field2': { +                u'help_text': u'', u'required': True, +                u'type': u'Single Character', u'label': 'field two'}}) diff --git a/rest_framework/views.py b/rest_framework/views.py index c5b89a02..11d50e5d 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -84,6 +84,8 @@ class APIView(View):                  if serializer is not None:                      field_name_types = {}                      for name, field in serializer.fields.iteritems(): +                        from rest_framework.fields import humanize_field +                        humanize_field(field)                          field_name_types[name] = field.__class__.__name__                  actions[method] = field_name_types | 
