Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically strip() all values in WTForms?

Tags:

python

wtforms

Is there any way to strip surrounding whitespace from all values in WTForms without adding a filter to every single field?

Currently I'm passing filters=[strip_whitespace] with the function shown below to my fields but having to repeat this for every field is quite ugly.

def strip_whitespace(s):
    if isinstance(s, basestring):
        s = s.strip()
    return s

A solution requiring subclassing of Form would be fine since I'm already doing that in my application.

like image 602
ThiefMaster Avatar asked Oct 07 '14 08:10

ThiefMaster


3 Answers

You can do it in WTForms 2.x by using the bind_field primitive on class Meta. The class Meta paradigm is a way to override WTForms behaviors in contexts such as binding/instantiating fields, rendering fields, and more.

Because anything overridden in class Meta defined on a Form is inherited to any form subclasses, you can use it to set up a base form class with your desired behaviors:

class MyBaseForm(Form):
    class Meta:
        def bind_field(self, form, unbound_field, options):
            filters = unbound_field.kwargs.get('filters', [])
            filters.append(my_strip_filter)
            return unbound_field.bind(form=form, filters=filters, **options)


def my_strip_filter(value):
    if value is not None and hasattr(value, 'strip'):
        return value.strip()
    return value

Now, just inherit MyBaseForm for all your forms and you're good to go.

like image 54
Crast Avatar answered Oct 17 '22 11:10

Crast


Unfortunately, I have no enough reputation to comment first response. But, there is extremely unpleasant bug in that example: When you do filters.append(smth) then on each form initialization filters growth by 1 element. As a result, your code works slower and slower until you restart it

Consider Example:

   class MyBaseForm(Form):
        class Meta:
            def bind_field(self, form, unbound_field, options):
                filters = unbound_field.kwargs.get('filters', [])
                filters.append(my_strip_filter)
                return unbound_field.bind(form=form, filters=filters, **options)


    def my_strip_filter(value):
        if value is not None and hasattr(value, 'strip'):
            return value.strip()
        return value

    class MyCustomForm(MyBaseForm):
        some_field = StringField(filters=[lambda x: x])

    for i in range(100):
        MyCustomForm(MultiDict({'some_field': 'erer'}))

    print(len(MyCustomForm.some_field.kwargs['filters']))  # print: 101

So the fast fix is to check that this filter not in list:

class MyBaseForm(Form):
    class Meta:
        def bind_field(self, form, unbound_field, options):
            filters = unbound_field.kwargs.get('filters', [])
            if my_strip_filter not in filters:
                filters.append(my_strip_filter)
        return unbound_field.bind(form=form, filters=filters, **options)
like image 7
Igor Avatar answered Oct 17 '22 11:10

Igor


I wouldn't be surprised if you could do it by subclassing form, but my solution was to just create custom Stripped* fields. I think this is at least better than passing filters every time because it is less error prone:

from wtforms import StringField, PasswordField


class Stripped(object):
    def process_formdata(self, valuelist):
        if valuelist:
            self.data = valuelist[0].strip()
        else:
            self.data = ''

class StrippedStringField(Stripped, StringField): pass
class StrippedPasswordField(Stripped, PasswordField): pass
like image 2
Thomas Dignan Avatar answered Oct 17 '22 13:10

Thomas Dignan