Commit 01c8c0ca by Tom Christie

Added help_text argument to fields

parent 234369ae
...@@ -39,6 +39,17 @@ except ImportError: ...@@ -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): ...@@ -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']
......
...@@ -101,7 +101,8 @@ class Field(object): ...@@ -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): ...@@ -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): ...@@ -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:
......
...@@ -15,6 +15,7 @@ from django.core.exceptions import ValidationError ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -597,33 +601,24 @@ class HyperlinkedModelSerializerOptions(ModelSerializerOptions):
class HyperlinkedModelSerializer(ModelSerializer): class HyperlinkedModelSerializer(ModelSerializer):
_options_class = HyperlinkedModelSerializerOptions _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): 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 = { kwargs = {
'view_name': self.get_default_view_name(self.opts.model) 'view_name': view_name
} }
if self.opts.lookup_field: if self.opts.lookup_field:
kwargs['lookup_field'] = self.opts.lookup_field kwargs['lookup_field'] = self.opts.lookup_field
return HyperlinkedIdentityField(**kwargs) 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): 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): ...@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment