Added humanized field names and types

This commit is contained in:
Oscar Vilaplana 2013-05-18 18:10:17 +02:00
parent 29739f78ba
commit 4dffcb5d77
3 changed files with 148 additions and 2 deletions

View File

@ -23,13 +23,44 @@ from django.utils.translation import ugettext_lazy as _
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from rest_framework import ISO_8601 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 BytesIO
from rest_framework.compat import six from rest_framework.compat import six
from rest_framework.compat import smart_text from rest_framework.compat import smart_text
from rest_framework.settings import api_settings 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): def is_simple_callable(obj):
""" """
True if the object is a callable that takes no arguments. 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): 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) return humanize_strptime(format)
@ -71,6 +103,61 @@ def readable_date_formats(formats):
return humanize_strptime(format) 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': (field.type_name if field.type_name
else humanize_field_type(field.form_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([(f.name, humanize_field(f)) for f in form.fields])
return fields
def readable_time_formats(formats): def readable_time_formats(formats):
format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]') format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]')
return humanize_strptime(format) return humanize_strptime(format)

View File

@ -4,12 +4,17 @@ General serializer field tests.
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
import datetime import datetime
from rest_framework.fields import humanize_field, humanize_field_type
from django import forms
from decimal import Decimal from decimal import Decimal
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from django.core import validators from django.core import validators
from rest_framework import serializers from rest_framework import serializers
from rest_framework.serializers import Serializer from rest_framework.serializers import Serializer
from rest_framework.fields import Field
from collections import namedtuple
from uuid import uuid4
class TimestampedModel(models.Model): class TimestampedModel(models.Model):
@ -685,3 +690,55 @@ class ChoiceFieldTests(TestCase):
""" """
f = serializers.ChoiceField(required=False, choices=self.SAMPLE_CHOICES) f = serializers.ChoiceField(required=False, choices=self.SAMPLE_CHOICES)
self.assertEqual(f.choices, models.fields.BLANK_CHOICE_DASH + self.SAMPLE_CHOICES) self.assertEqual(f.choices, models.fields.BLANK_CHOICE_DASH + self.SAMPLE_CHOICES)
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)

View File

@ -80,6 +80,8 @@ class APIView(View):
if serializer is not None: if serializer is not None:
field_name_types = {} field_name_types = {}
for name, field in serializer.fields.iteritems(): for name, field in serializer.fields.iteritems():
from rest_framework.fields import humanize_field
humanize_field(field)
field_name_types[name] = field.__class__.__name__ field_name_types[name] = field.__class__.__name__
actions[method] = field_name_types actions[method] = field_name_types