mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-30 23:47: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