mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-29 21:14:01 +03:00
Added humanized field names and types
This commit is contained in:
parent
29739f78ba
commit
4dffcb5d77
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user