aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2014-09-10 13:52:16 +0100
committerTom Christie2014-09-10 13:52:16 +0100
commit01c8c0cad977fc0787dbfc78bd34f4fd37e613f4 (patch)
tree0bb60e5f7c419555d583e50d4fef8cb658d11ca1
parent234369aefdf08d7d0161d851866990754c00d31f (diff)
downloaddjango-rest-framework-01c8c0cad977fc0787dbfc78bd34f4fd37e613f4.tar.bz2
Added help_text argument to fields
-rw-r--r--rest_framework/compat.py23
-rw-r--r--rest_framework/fields.py5
-rw-r--r--rest_framework/serializers.py86
3 files changed, 56 insertions, 58 deletions
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index fa0f0bfb..70b38df9 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -39,6 +39,17 @@ except ImportError:
django_filters = None
+if django.VERSION >= (1, 6):
+ def clean_manytomany_helptext(text):
+ return text
+else:
+ # Up to version 1.5 many to many fields automatically suffix
+ # the `help_text` attribute with hardcoded text.
+ def clean_manytomany_helptext(text):
+ if text.endswith(' Hold down "Control", or "Command" on a Mac, to select more than one.'):
+ text = text[:-69]
+ return text
+
# Django-guardian is optional. Import only if guardian is in INSTALLED_APPS
# Fixes (#1712). We keep the try/except for the test suite.
guardian = None
@@ -99,18 +110,8 @@ def get_concrete_model(model_cls):
return model_cls
-# View._allowed_methods only present from 1.5 onwards
-if django.VERSION >= (1, 5):
- from django.views.generic import View
-else:
- from django.views.generic import View as DjangoView
-
- class View(DjangoView):
- def _allowed_methods(self):
- return [m.upper() for m in self.http_method_names if hasattr(self, m)]
-
-
# PATCH method is not implemented by Django
+from django.views.generic import View
if 'patch' not in View.http_method_names:
View.http_method_names = View.http_method_names + ['patch']
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index e2bd5700..e71dce1d 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -101,7 +101,8 @@ class Field(object):
def __init__(self, read_only=False, write_only=False,
required=None, default=empty, initial=None, source=None,
- label=None, style=None, error_messages=None, validators=[]):
+ label=None, help_text=None, style=None,
+ error_messages=None, validators=[]):
self._creation_counter = Field._creation_counter
Field._creation_counter += 1
@@ -122,6 +123,7 @@ class Field(object):
self.source = source
self.initial = initial
self.label = label
+ self.help_text = help_text
self.style = {} if style is None else style
self.validators = validators or self.default_validators[:]
@@ -372,7 +374,6 @@ class IntegerField(Field):
self.validators.append(validators.MaxValueValidator(max_value))
if min_value is not None:
self.validators.append(validators.MinValueValidator(min_value))
- print self.__class__.__name__, self.validators
def to_native(self, data):
try:
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 0727b8cd..459f8a8c 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -15,6 +15,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.utils import six
from collections import namedtuple, OrderedDict
+from rest_framework.compat import clean_manytomany_helptext
from rest_framework.fields import empty, set_value, Field, SkipField
from rest_framework.settings import api_settings
from rest_framework.utils import html, modelinfo, representation
@@ -117,8 +118,9 @@ class SerializerMetaclass(type):
"""
This metaclass sets a dictionary named `base_fields` on the class.
- Any fields included as attributes on either the class or it's superclasses
- will be include in the `base_fields` dictionary.
+ Any instances of `Field` included as attributes on either the class
+ or on any of its superclasses will be include in the
+ `base_fields` dictionary.
"""
@classmethod
@@ -379,6 +381,10 @@ class ModelSerializer(Serializer):
info = modelinfo.get_field_info(self.opts.model)
ret = OrderedDict()
+ serializer_url_field = self.get_url_field()
+ if serializer_url_field:
+ ret[api_settings.URL_FIELD_NAME] = serializer_url_field
+
serializer_pk_field = self.get_pk_field(info.pk)
if serializer_pk_field:
ret[info.pk.name] = serializer_pk_field
@@ -404,6 +410,9 @@ class ModelSerializer(Serializer):
return ret
+ def get_url_field(self):
+ return None
+
def get_pk_field(self, model_field):
"""
Returns a default instance of the pk field.
@@ -446,13 +455,14 @@ class ModelSerializer(Serializer):
if model_field:
if model_field.null or model_field.blank:
kwargs['required'] = False
- # if model_field.help_text is not None:
- # kwargs['help_text'] = model_field.help_text
- if model_field.verbose_name is not None:
+ if model_field.verbose_name:
kwargs['label'] = model_field.verbose_name
if not model_field.editable:
kwargs['read_only'] = True
kwargs.pop('queryset', None)
+ help_text = clean_manytomany_helptext(model_field.help_text)
+ if help_text:
+ kwargs['help_text'] = help_text
return PrimaryKeyRelatedField(**kwargs)
@@ -469,6 +479,9 @@ class ModelSerializer(Serializer):
if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name
+ if model_field.help_text:
+ kwargs['help_text'] = model_field.help_text
+
if isinstance(model_field, models.AutoField) or not model_field.editable:
kwargs['read_only'] = True
# Read only implies that the field is not required.
@@ -481,6 +494,14 @@ class ModelSerializer(Serializer):
# We have a cleaner repr on the instance if we don't set it.
kwargs.pop('required', None)
+ if model_field.flatchoices:
+ # If this model field contains choices, then use a ChoiceField,
+ # rather than the standard serializer field for this type.
+ # Note that we return this prior to setting any validation type
+ # keyword arguments, as those are not valid initializers.
+ kwargs['choices'] = model_field.flatchoices
+ return ChoiceField(**kwargs)
+
# Ensure that max_length is passed explicitly as a keyword arg,
# rather than as a validator.
max_length = getattr(model_field, 'max_length', None)
@@ -561,23 +582,6 @@ class ModelSerializer(Serializer):
if validator_kwarg:
kwargs['validators'] = validator_kwarg
- # if issubclass(model_field.__class__, models.TextField):
- # kwargs['widget'] = widgets.Textarea
-
- # if model_field.help_text is not None:
- # kwargs['help_text'] = model_field.help_text
-
- # TODO: TypedChoiceField?
- if model_field.flatchoices: # This ModelField contains choices
- kwargs['choices'] = model_field.flatchoices
- if model_field.null:
- kwargs['empty'] = None
- return ChoiceField(**kwargs)
-
- if model_field.null and \
- issubclass(model_field.__class__, (models.CharField, models.TextField)):
- kwargs['allow_none'] = True
-
try:
return self.field_mapping[model_field.__class__](**kwargs)
except KeyError:
@@ -597,33 +601,24 @@ class HyperlinkedModelSerializerOptions(ModelSerializerOptions):
class HyperlinkedModelSerializer(ModelSerializer):
_options_class = HyperlinkedModelSerializerOptions
- def get_default_fields(self):
- fields = super(HyperlinkedModelSerializer, self).get_default_fields()
-
- if self.opts.view_name is None:
- self.opts.view_name = self.get_default_view_name(self.opts.model)
-
- url_field_name = api_settings.URL_FIELD_NAME
- if url_field_name not in fields:
- ret = fields.__class__()
- ret[url_field_name] = self.get_url_field()
- ret.update(fields)
- fields = ret
-
- return fields
-
- def get_pk_field(self, model_field):
- if self.opts.fields and model_field.name in self.opts.fields:
- return self.get_field(model_field)
-
def get_url_field(self):
+ if self.opts.view_name is not None:
+ view_name = self.opts.view_name
+ else:
+ view_name = self.get_default_view_name(self.opts.model)
+
kwargs = {
- 'view_name': self.get_default_view_name(self.opts.model)
+ 'view_name': view_name
}
if self.opts.lookup_field:
kwargs['lookup_field'] = self.opts.lookup_field
+
return HyperlinkedIdentityField(**kwargs)
+ def get_pk_field(self, model_field):
+ if self.opts.fields and model_field.name in self.opts.fields:
+ return self.get_field(model_field)
+
def get_related_field(self, model_field, related_model, to_many, has_through_model):
"""
Creates a default instance of a flat relational field.
@@ -643,13 +638,14 @@ class HyperlinkedModelSerializer(ModelSerializer):
if model_field:
if model_field.null or model_field.blank:
kwargs['required'] = False
- # if model_field.help_text is not None:
- # kwargs['help_text'] = model_field.help_text
- if model_field.verbose_name is not None:
+ if model_field.verbose_name:
kwargs['label'] = model_field.verbose_name
if not model_field.editable:
kwargs['read_only'] = True
kwargs.pop('queryset', None)
+ help_text = clean_manytomany_helptext(model_field.help_text)
+ if help_text:
+ kwargs['help_text'] = help_text
return HyperlinkedRelatedField(**kwargs)