From 643d3491a65237fef6932ef8833472c243ad7ee8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 18 Oct 2012 23:48:52 +0100 Subject: First pass at pastebin tutorial --- rest_framework/fields.py | 80 ++++++++++++++++++++++++++++++++++++++++----- rest_framework/renderers.py | 13 ++++++-- 2 files changed, 82 insertions(+), 11 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index bb9a523d..14422b27 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -7,6 +7,7 @@ from django.core import validators from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.urlresolvers import resolve from django.conf import settings +from django.forms import widgets from django.utils.encoding import is_protected_type, smart_unicode from django.utils.translation import ugettext_lazy as _ from rest_framework.reverse import reverse @@ -105,10 +106,14 @@ class WritableField(Field): 'required': _('This field is required.'), 'invalid': _('Invalid value.'), } + widget = widgets.TextInput def __init__(self, source=None, readonly=False, required=None, - validators=[], error_messages=None): + validators=[], error_messages=None, widget=None, + help_text=None, initial=None): + super(WritableField, self).__init__(source=source) + self.readonly = readonly if required is None: self.required = not(readonly) @@ -124,6 +129,15 @@ class WritableField(Field): self.validators = self.default_validators + validators + # These attributes are ony used for HTML forms. + self.initial = initial + self.help_text = help_text and smart_unicode(help_text) or '' + + widget = widget or self.widget + if isinstance(widget, type): + widget = widget() + self.widget = widget + def validate(self, value): if value in validators.EMPTY_VALUES and self.required: raise ValidationError(self.error_messages['required']) @@ -157,9 +171,12 @@ class WritableField(Field): try: native = data[field_name] except KeyError: - if self.required: - raise ValidationError(self.error_messages['required']) - return + if getattr(self, 'missing_value', None) is not None: + native = self.missing_value + else: + if self.required: + raise ValidationError(self.error_messages['required']) + return value = self.from_native(native) if self.source == '*': @@ -394,20 +411,19 @@ class HyperlinkedIdentityField(Field): class BooleanField(WritableField): type_name = 'BooleanField' + widget = widgets.CheckboxInput default_error_messages = { 'invalid': _(u"'%s' value must be either True or False."), } + empty = False + missing_value = False # Fill in missing value not supplied by html form def from_native(self, value): - if value in (True, False): - # if value is 1 or 0 than it's equal to True or False, but we want - # to return a true bool for semantic reasons. - return bool(value) if value in ('t', 'True', '1'): return True if value in ('f', 'False', '0'): return False - raise ValidationError(self.error_messages['invalid'] % value) + return bool(value) class CharField(WritableField): @@ -427,6 +443,52 @@ class CharField(WritableField): return smart_unicode(value) +class ChoiceField(WritableField): + type_name = 'ChoiceField' + widget = widgets.Select + default_error_messages = { + 'invalid_choice': _('Select a valid choice. %(value)s is not one of the available choices.'), + } + + def __init__(self, choices=(), *args, **kwargs): + super(ChoiceField, self).__init__(*args, **kwargs) + self.choices = choices + + def _get_choices(self): + return self._choices + + def _set_choices(self, value): + # Setting choices also sets the choices on the widget. + # choices can be any iterable, but we call list() on it because + # it will be consumed more than once. + self._choices = self.widget.choices = list(value) + + choices = property(_get_choices, _set_choices) + + def validate(self, value): + """ + Validates that the input is in self.choices. + """ + super(ChoiceField, self).validate(value) + if value and not self.valid_value(value): + raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) + + def valid_value(self, value): + """ + Check to see if the provided value is a valid choice. + """ + for k, v in self.choices: + if isinstance(v, (list, tuple)): + # This is an optgroup, so look inside the group for options + for k2, v2 in v: + if value == smart_unicode(k2): + return True + else: + if value == smart_unicode(k): + return True + return False + + class EmailField(CharField): type_name = 'EmailField' diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 23fd961b..936bec36 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -279,13 +279,22 @@ class BrowsableAPIRenderer(BaseRenderer): continue kwargs = {} + kwargs['required'] = v.required if getattr(v, 'queryset', None): - kwargs['queryset'] = getattr(v, 'queryset', None) + kwargs['queryset'] = v.queryset + if getattr(v, 'widget', None): + kwargs['widget'] = v.widget + if getattr(v, 'initial', None): + kwargs['initial'] = v.initial + if getattr(v, 'help_text', None): + kwargs['help_text'] = v.help_text + kwargs['label'] = k + print kwargs try: fields[k] = field_mapping[v.__class__](**kwargs) except KeyError: - fields[k] = forms.CharField() + fields[k] = forms.CharField(**kwargs) OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) if obj and not view.request.method == 'DELETE': # Don't fill in the form when the object is deleted -- cgit v1.2.3 From dab177e29e45b657bb43705979c8e601d5a1b31b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 19 Oct 2012 09:20:54 +0100 Subject: Drop help_text --- rest_framework/fields.py | 3 +-- rest_framework/renderers.py | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 14422b27..0990eadc 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -110,7 +110,7 @@ class WritableField(Field): def __init__(self, source=None, readonly=False, required=None, validators=[], error_messages=None, widget=None, - help_text=None, initial=None): + initial=None): super(WritableField, self).__init__(source=source) @@ -131,7 +131,6 @@ class WritableField(Field): # These attributes are ony used for HTML forms. self.initial = initial - self.help_text = help_text and smart_unicode(help_text) or '' widget = widget or self.widget if isinstance(widget, type): diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 936bec36..de2276ad 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -286,8 +286,6 @@ class BrowsableAPIRenderer(BaseRenderer): kwargs['widget'] = v.widget if getattr(v, 'initial', None): kwargs['initial'] = v.initial - if getattr(v, 'help_text', None): - kwargs['help_text'] = v.help_text kwargs['label'] = k print kwargs -- cgit v1.2.3 From a7390fe7044c4f10d15fdbe9de9e594d0ffa2e05 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 19 Oct 2012 09:47:01 +0100 Subject: Fix up widget choices --- rest_framework/renderers.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index de2276ad..ba5489bc 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -6,6 +6,7 @@ on the response, such as JSON encoded data or HTML output. REST framework also provides an HTML renderer the renders the browseable API. """ +import copy import string from django import forms from django.http.multipartparser import parse_header @@ -283,11 +284,19 @@ class BrowsableAPIRenderer(BaseRenderer): if getattr(v, 'queryset', None): kwargs['queryset'] = v.queryset if getattr(v, 'widget', None): - kwargs['widget'] = v.widget + widget = copy.deepcopy(v.widget) + # If choices have friendly readable names, + # then add in the identities too + if getattr(widget, 'choices', None): + choices = widget.choices + if any([ident != desc for (ident, desc) in choices]): + choices = [(ident, "%s (%s)" % (desc, ident)) + for (ident, desc) in choices] + widget.choices = choices + kwargs['widget'] = widget if getattr(v, 'initial', None): kwargs['initial'] = v.initial kwargs['label'] = k - print kwargs try: fields[k] = field_mapping[v.__class__](**kwargs) -- cgit v1.2.3 From 93f1aa4f69df85add114c9730a01b50d013a844a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 21 Oct 2012 17:41:05 +0100 Subject: Remove `initial` kwarg, add `default`. --- rest_framework/fields.py | 18 +++++++++++------- rest_framework/renderers.py | 8 ++++++-- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 0990eadc..29940946 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -107,10 +107,11 @@ class WritableField(Field): 'invalid': _('Invalid value.'), } widget = widgets.TextInput + default = None def __init__(self, source=None, readonly=False, required=None, validators=[], error_messages=None, widget=None, - initial=None): + default=None): super(WritableField, self).__init__(source=source) @@ -128,10 +129,9 @@ class WritableField(Field): self.error_messages = messages self.validators = self.default_validators + validators + self.default = default or self.default - # These attributes are ony used for HTML forms. - self.initial = initial - + # Widgets are ony used for HTML forms. widget = widget or self.widget if isinstance(widget, type): widget = widget() @@ -170,8 +170,8 @@ class WritableField(Field): try: native = data[field_name] except KeyError: - if getattr(self, 'missing_value', None) is not None: - native = self.missing_value + if self.default is not None: + native = self.default else: if self.required: raise ValidationError(self.error_messages['required']) @@ -415,7 +415,11 @@ class BooleanField(WritableField): 'invalid': _(u"'%s' value must be either True or False."), } empty = False - missing_value = False # Fill in missing value not supplied by html form + + # Note: we set default to `False` in order to fill in missing value not + # supplied by html form. TODO: Fix so that only html form input gets + # this behavior. + default = False def from_native(self, value): if value in ('t', 'True', '1'): diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index ba5489bc..b2dbffd2 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -281,8 +281,10 @@ class BrowsableAPIRenderer(BaseRenderer): kwargs = {} kwargs['required'] = v.required + if getattr(v, 'queryset', None): kwargs['queryset'] = v.queryset + if getattr(v, 'widget', None): widget = copy.deepcopy(v.widget) # If choices have friendly readable names, @@ -294,8 +296,10 @@ class BrowsableAPIRenderer(BaseRenderer): for (ident, desc) in choices] widget.choices = choices kwargs['widget'] = widget - if getattr(v, 'initial', None): - kwargs['initial'] = v.initial + + if getattr(v, 'default', None) is not None: + kwargs['initial'] = v.default + kwargs['label'] = k try: -- cgit v1.2.3