mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-13 10:00:53 +03:00
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
This commit is contained in:
commit
843ae60237
|
@ -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': 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):
|
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)
|
||||||
|
|
|
@ -4,6 +4,9 @@ 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,
|
||||||
|
humanize_form_fields)
|
||||||
|
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
|
||||||
|
@ -11,6 +14,9 @@ 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.tests.models import RESTFrameworkModel
|
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):
|
class TimestampedModel(models.Model):
|
||||||
|
@ -809,3 +815,75 @@ class URLFieldTests(TestCase):
|
||||||
serializer = URLFieldSerializer(data={})
|
serializer = URLFieldSerializer(data={})
|
||||||
self.assertEqual(serializer.is_valid(), True)
|
self.assertEqual(serializer.is_valid(), True)
|
||||||
self.assertEqual(getattr(serializer.fields['url_field'], 'max_length'), 20)
|
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'}})
|
||||||
|
|
|
@ -84,6 +84,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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user