Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Field unexpectedly assumes default value when formdata is empty #157

Closed
antonv6 opened this issue Jan 28, 2015 · 6 comments
Closed

Field unexpectedly assumes default value when formdata is empty #157

antonv6 opened this issue Jan 28, 2015 · 6 comments

Comments

@antonv6
Copy link

antonv6 commented Jan 28, 2015

import wtforms
from tests.common import DummyPostData


class FooForm(wtforms.Form):
    foo = wtforms.fields.StringField('Foo')


def mkform(formdata):
    return FooForm(DummyPostData(formdata))


print repr(mkform({}).foo.data)
print repr(mkform({'totally': 'unrelated'}).foo.data)

Produces:

None
u''
@bootandy
Copy link
Contributor

bootandy commented Feb 2, 2015

if formdata is resolved to False if an empty dict is passed in.
Perhaps it should be "if formdata is not None"
https://github.com/wtforms/wtforms/blob/master/wtforms/fields/core.py#L277

@PiTiLeZarD
Copy link

I don't know if this is being read but I'll give it a try:

I also have problem with the default values:
If you have a StringField with default value as None and you pass a formdata which either specifies the value to None or does not specify the value, the values assigned to field.data is '' (empty string):
https://github.com/wtforms/wtforms/blob/master/wtforms/fields/core.py#L526

Which in turn with a populate_obj will override the database null values with empty strings... And that makes me sad!

It's not super clean but this fixes my problem so far:

class Form(WTForm):

    def populate_obj(self, obj):
        for field_name, value in self.raw_data.iteritems():
            if value is None:
                self[field_name].data = value
        return super(Form, self).populate_obj(obj)

    @property
    def raw_data(self):
        return {name: None if not f.raw_data or not len(f.raw_data) else f.data
                for name, f in self._fields.iteritems()}

I did not overwrite the StringField because I imagine if the logic of using the default value is not done here, it's probably not done anywhere... Can someone explain why do we not use the default value (and have a default default value?)?

Especially because just underneath, the function _value() has a test for None, so if we need the StringTyped value we can call that one and the data stays None which is good right?

Thanks for your time ;)

@prencher
Copy link
Contributor

prencher commented Mar 2, 2015

If formdata is provided, it overrides any and all defaults. This is intended behavior, and it has to do with how HTML forms work. For example, checkboxes do not send a value when they are not checked.

If you want to use None when you get empty strings, you should check for it and set it appropriately. You can use a filter to do this. Something like:

username = StringField(u'username', filters=[lambda v: None if v == '' else v])

More info here: http://wtforms.readthedocs.org/en/latest/faq.html#why-does-blank-input-not-go-back-to-the-default-value

@prencher prencher closed this as completed Mar 2, 2015
@PiTiLeZarD
Copy link

Alright, that shines some light on the "why you do not use the default value" and I tend to agree with this now... it does not explain the "why do you then replace the data by a random arbitrary value" (in this case empty string but why not "N/A" or anything really?

If I provide form data and this field is not in the form data, setting the data to empty string is the exact same as setting it to the default value...

I'm saying that because I use WTForms not to display forms but to constraint and validate a wheezy.web REST server, when a field is not required, it's then optional, and following the logic of consistency by not using the default value, it should also not have a default empty string and just be left as it is.

The filter solution is rather localised and I should copy/paste that all over the place if I want to use this solution. For now I'll stick with my custom populate_obj but just have a think about the necessity of the

    else:
        self.data = ''

Thanks heaps for your time!

@prencher
Copy link
Contributor

prencher commented Mar 3, 2015

WTForms is currently optimized for HTML forms, not REST. As for values, we wouldn't want to make assumptions about what you want when the field isn't provided, so we provide reasonable defaults such as the empty string and 0.

A better solution for your problem would be to override process_formdata.

@Bekt
Copy link

Bekt commented Nov 14, 2015

For people coming here from search engines, I wrote a small solution here: http://bekt.github.io/p/wtforms-patch/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants