Added help_text argument to fields

This commit is contained in:
Tom Christie 2014-09-10 13:52:16 +01:00
parent 234369aefd
commit 01c8c0cad9
3 changed files with 56 additions and 58 deletions

View File

@ -39,6 +39,17 @@ except ImportError:
django_filters = None 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 # Django-guardian is optional. Import only if guardian is in INSTALLED_APPS
# Fixes (#1712). We keep the try/except for the test suite. # Fixes (#1712). We keep the try/except for the test suite.
guardian = None guardian = None
@ -99,18 +110,8 @@ def get_concrete_model(model_cls):
return 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 # PATCH method is not implemented by Django
from django.views.generic import View
if 'patch' not in View.http_method_names: if 'patch' not in View.http_method_names:
View.http_method_names = View.http_method_names + ['patch'] View.http_method_names = View.http_method_names + ['patch']

View File

@ -101,7 +101,8 @@ class Field(object):
def __init__(self, read_only=False, write_only=False, def __init__(self, read_only=False, write_only=False,
required=None, default=empty, initial=None, source=None, 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 self._creation_counter = Field._creation_counter
Field._creation_counter += 1 Field._creation_counter += 1
@ -122,6 +123,7 @@ class Field(object):
self.source = source self.source = source
self.initial = initial self.initial = initial
self.label = label self.label = label
self.help_text = help_text
self.style = {} if style is None else style self.style = {} if style is None else style
self.validators = validators or self.default_validators[:] self.validators = validators or self.default_validators[:]
@ -372,7 +374,6 @@ class IntegerField(Field):
self.validators.append(validators.MaxValueValidator(max_value)) self.validators.append(validators.MaxValueValidator(max_value))
if min_value is not None: if min_value is not None:
self.validators.append(validators.MinValueValidator(min_value)) self.validators.append(validators.MinValueValidator(min_value))
print self.__class__.__name__, self.validators
def to_native(self, data): def to_native(self, data):
try: try:

View File

@ -15,6 +15,7 @@ from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils import six from django.utils import six
from collections import namedtuple, OrderedDict 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.fields import empty, set_value, Field, SkipField
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.utils import html, modelinfo, representation 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. This metaclass sets a dictionary named `base_fields` on the class.
Any fields included as attributes on either the class or it's superclasses Any instances of `Field` included as attributes on either the class
will be include in the `base_fields` dictionary. or on any of its superclasses will be include in the
`base_fields` dictionary.
""" """
@classmethod @classmethod
@ -379,6 +381,10 @@ class ModelSerializer(Serializer):
info = modelinfo.get_field_info(self.opts.model) info = modelinfo.get_field_info(self.opts.model)
ret = OrderedDict() 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) serializer_pk_field = self.get_pk_field(info.pk)
if serializer_pk_field: if serializer_pk_field:
ret[info.pk.name] = serializer_pk_field ret[info.pk.name] = serializer_pk_field
@ -404,6 +410,9 @@ class ModelSerializer(Serializer):
return ret return ret
def get_url_field(self):
return None
def get_pk_field(self, model_field): def get_pk_field(self, model_field):
""" """
Returns a default instance of the pk field. Returns a default instance of the pk field.
@ -446,13 +455,14 @@ class ModelSerializer(Serializer):
if model_field: if model_field:
if model_field.null or model_field.blank: if model_field.null or model_field.blank:
kwargs['required'] = False kwargs['required'] = False
# if model_field.help_text is not None: if model_field.verbose_name:
# kwargs['help_text'] = model_field.help_text
if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name kwargs['label'] = model_field.verbose_name
if not model_field.editable: if not model_field.editable:
kwargs['read_only'] = True kwargs['read_only'] = True
kwargs.pop('queryset', None) kwargs.pop('queryset', None)
help_text = clean_manytomany_helptext(model_field.help_text)
if help_text:
kwargs['help_text'] = help_text
return PrimaryKeyRelatedField(**kwargs) return PrimaryKeyRelatedField(**kwargs)
@ -469,6 +479,9 @@ class ModelSerializer(Serializer):
if model_field.verbose_name is not None: if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name 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: if isinstance(model_field, models.AutoField) or not model_field.editable:
kwargs['read_only'] = True kwargs['read_only'] = True
# Read only implies that the field is not required. # 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. # We have a cleaner repr on the instance if we don't set it.
kwargs.pop('required', None) 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, # Ensure that max_length is passed explicitly as a keyword arg,
# rather than as a validator. # rather than as a validator.
max_length = getattr(model_field, 'max_length', None) max_length = getattr(model_field, 'max_length', None)
@ -561,23 +582,6 @@ class ModelSerializer(Serializer):
if validator_kwarg: if validator_kwarg:
kwargs['validators'] = 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: try:
return self.field_mapping[model_field.__class__](**kwargs) return self.field_mapping[model_field.__class__](**kwargs)
except KeyError: except KeyError:
@ -597,33 +601,24 @@ class HyperlinkedModelSerializerOptions(ModelSerializerOptions):
class HyperlinkedModelSerializer(ModelSerializer): class HyperlinkedModelSerializer(ModelSerializer):
_options_class = HyperlinkedModelSerializerOptions _options_class = HyperlinkedModelSerializerOptions
def get_default_fields(self): def get_url_field(self):
fields = super(HyperlinkedModelSerializer, self).get_default_fields() 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)
if self.opts.view_name is None: kwargs = {
self.opts.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
url_field_name = api_settings.URL_FIELD_NAME return HyperlinkedIdentityField(**kwargs)
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): def get_pk_field(self, model_field):
if self.opts.fields and model_field.name in self.opts.fields: if self.opts.fields and model_field.name in self.opts.fields:
return self.get_field(model_field) return self.get_field(model_field)
def get_url_field(self):
kwargs = {
'view_name': self.get_default_view_name(self.opts.model)
}
if self.opts.lookup_field:
kwargs['lookup_field'] = self.opts.lookup_field
return HyperlinkedIdentityField(**kwargs)
def get_related_field(self, model_field, related_model, to_many, has_through_model): def get_related_field(self, model_field, related_model, to_many, has_through_model):
""" """
Creates a default instance of a flat relational field. Creates a default instance of a flat relational field.
@ -643,13 +638,14 @@ class HyperlinkedModelSerializer(ModelSerializer):
if model_field: if model_field:
if model_field.null or model_field.blank: if model_field.null or model_field.blank:
kwargs['required'] = False kwargs['required'] = False
# if model_field.help_text is not None: if model_field.verbose_name:
# kwargs['help_text'] = model_field.help_text
if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name kwargs['label'] = model_field.verbose_name
if not model_field.editable: if not model_field.editable:
kwargs['read_only'] = True kwargs['read_only'] = True
kwargs.pop('queryset', None) kwargs.pop('queryset', None)
help_text = clean_manytomany_helptext(model_field.help_text)
if help_text:
kwargs['help_text'] = help_text
return HyperlinkedRelatedField(**kwargs) return HyperlinkedRelatedField(**kwargs)