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 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': (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):
format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]')
return humanize_strptime(format)

View File

@ -4,12 +4,17 @@ 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
from django import forms
from decimal import Decimal
from django.db import models
from django.test import TestCase
from django.core import validators
from rest_framework import serializers
from rest_framework.serializers import Serializer
from rest_framework.fields import Field
from collections import namedtuple
from uuid import uuid4
class TimestampedModel(models.Model):
@ -685,3 +690,55 @@ class ChoiceFieldTests(TestCase):
"""
f = serializers.ChoiceField(required=False, choices=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:
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