Automatically strip() all values in WTForms?
Asked Answered
D

3

17

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.

Doykos answered 7/10, 2014 at 8:52 Comment(0)
T
14

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.

Tarkington answered 18/1, 2015 at 21:43 Comment(3)
I think it has to be get instead of pop or any but the first Form will not have any custom filters. Also, that line can be simplified a bit: filters = unbound_field.kwargs.get('filters', [])Doykos
This cannot be done on FormField as FormField cannot take filters, so check before not issubclass(unbound_field.field_class, FormField)Baldwin
Thanks @gdoumenc, good point. I think for simplicity, I'll leave the code example as is... it illustrates the point without getting too far into specifics. Theoretically you would also leave the strip filter out for non-text fields, and so on.Tarkington
N
12

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)
Nsf answered 2/2, 2018 at 17:49 Comment(0)
S
3

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
Sexless answered 29/12, 2014 at 21:54 Comment(1)
Not really a good solution since it requires subclassing every single field (WTForms has lots of them)Doykos

© 2022 - 2024 — McMap. All rights reserved.