Use unambiguously magic value for None choices

WTForms coerces `None` as a choice to `'None'` as a string when
rendering form fields (form fields will only ever have string data
because that what the browser posts back).

But internally WTForms coerces `None` to mean an unset value, ie where
the user hasn’t selected a radio button:
283b280320/src/wtforms/utils.py (L1-L20)

We shouldn’t use `None` to mean two different things. And in fact we
can’t, because it in effect means that we’re always getting a value
for the `move_to` field, even if the user hasn’t chosen to move any
templates. Which results in some very expected behaviour.
This commit is contained in:
Chris Hill-Scott
2018-12-03 17:29:23 +00:00
parent 4c148c7c23
commit 2fcf278a5b
3 changed files with 39 additions and 17 deletions

View File

@@ -707,24 +707,26 @@ class ServicePostageForm(StripWhitespaceForm):
class FieldWithNoneOption():
# This needs to match the data the browser will post from
# <input value='None'>
NONE_OPTION_VALUE = 'None'
# This is a special value that is specific to our forms. This is
# more expicit than casting `None` to a string `'None'` which can
# have unexpected edge cases
NONE_OPTION_VALUE = '__NONE__'
# When receiving Python data, eg when instantiating the form object
# we want to convert that data to our special value, so that it gets
# recognised as being one of the valid choices
def process_data(self, value):
self.data = self.NONE_OPTION_VALUE if value is None else value
# After validation we want to convert it back to a Python `None` for
# use elsewhere, eg posting to the API
def post_validate(self, form, validation_stopped):
if self.data == self.NONE_OPTION_VALUE:
if self.data == self.NONE_OPTION_VALUE and not validation_stopped:
self.data = None
class RadioFieldWithNoneOption(FieldWithNoneOption, RadioField):
def validate(self, *args, **kwargs):
if self.data == self.NONE_OPTION_VALUE:
self.data = None
return True
return super().validate(*args, **kwargs)
pass
class HiddenFieldWithNoneOption(FieldWithNoneOption, HiddenField):
@@ -1194,10 +1196,13 @@ class TemplateAndFoldersSelectionForm(Form):
self.op = None
self.is_move_op = self.is_add_folder_op = self.is_add_template_op = False
if current_folder_id is None:
current_folder_id = RadioFieldWithNoneOption.NONE_OPTION_VALUE
self.move_to.choices = [
(item['id'], item['name'])
for item in ([self.ALL_TEMPLATES_FOLDER] + all_template_folders)
if item['id'] != str(current_folder_id)
if item['id'] != current_folder_id
]
self.add_template_by_template_type.choices = list(filter(None, [
@@ -1237,5 +1242,6 @@ class TemplateAndFoldersSelectionForm(Form):
move_to_new_folder_name = StringField('Folder name', validators=[required_for_ops('move_to_new_folder')])
add_template_by_template_type = RadioFieldWithNoneOption('Add new', validators=[
Optional(),
required_for_ops('add_template')
])