10

I'm having trouble populating a form using a dictionary:

        row = {'firstname':'Bob', 'lastname': "Smith",
               'email': 'bob@bubba.com', 'phone': '512.999.1212'}
        form = RolodexEntry(obj=row)

doesn't put any data into form (i.e. form.firstname.data = None after the preceding).

The top of the form definition is shown below. I'm at a loss for what to try next. The form documentation just says:

obj – If formdata is empty or not provided, this object is checked for attributes matching form field names, which will be used for field values.

class RolodexEntry(Form):
    firstname  = TextField('First Name',[validators.length(max=40)],
                           filters=[strip_filter])
    lastname   = TextField('Last Name', [validators.length(max=40)],
                           filters=[strip_filter])
    email      = TextField('Email',     [validators.Optional(),
                                         validators.length(max=25),
                                         validators.Email()],
                           filters=[strip_filter])
    ...
pgoetz
  • 848
  • 1
  • 8
  • 18

5 Answers5

18

The issue is the WTForms only use getattr to check if the field name exists in obj (it doesn't try to invoke __getitem__). You can use a namedtuple instead of a dictionary or you can pass in your data as keyword arguments (form = RolodexEntry(**row)).

Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • 5
    Overriding `process` is a bad idea. One can simply pass the dictionary as `**row` to the Form constructor if a dict is required. – Crast May 02 '13 at 21:49
10

This answer included for completeness. As pointed out by Sean Vieira, WTForms is using getattr to get attribute names, which doesn't work with dictionaries. Using the accepted answer from

Convert Python dict to object?

This also works:

class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)

row = {'firstname':'Bob', 'lastname': "Smith",
       'email': 'bob@bubba.com', 'phone': '512.999.1212'}
rowobj = Struct(**row)
form = RolodexEntry(obj=rowobj)
Community
  • 1
  • 1
pgoetz
  • 848
  • 1
  • 8
  • 18
  • This worked for me over the accepted answer. Also in my case I had to add `RolodexEntry(formdata=None, obj=rowobj)`. – wgwz Feb 06 '16 at 21:41
1

Albeit this question was asked a while ago, I suggest to see Sean Vieira's answer to a duplicate of this question. As explained in his answer, any class with an interface including getlist will be accepted by the wtforms.Form constructor.

Community
  • 1
  • 1
Philippe Hebert
  • 1,616
  • 2
  • 24
  • 51
1

according to Sean Vieira 's answer and in my flask app, I write my code like this:

from collections import namedtuple
UpdateSchema= namedtuple('UpdateSchema', ['name', 'real_name', 'email', 'phone'])
update_schema = UpdateSchema(
    name= current_user.name,
    real_name=current_user.job_hunter.real_name,
    email=current_user.email,
    phone=current_user.job_hunter.phone
)
form = UpdateJobHunterForm(obj=update_schema)

my app is a job find website, and I suggest you do not write like :

update_schema = dict(
    'name': current_user.name,
    'real_name':current_user.job_hunter.real_name,
    'email':current_user.email,
    'phone':current_user.job_hunter.phone
)
form = UpdateJobHunterForm(**update_schema)

In this way, if I want to upload a file, the request wont get the file filed data, so do not write in the second way!!

Carl Lee
  • 732
  • 5
  • 5
0

When I was using flask_wtf, I found Flask's form class was messing me up. I think this was the same issue from @wgwz's comment to @pgoetz's answer. The Flask form automatically populated formdata with data from the request. Since my "RolodexEntry" was a FormField embedded in a larger Form, the automatically-populated formdata was invalid. To try to fix this, instead of subclassing Flask's Form, I subclassed wtforms' Form.

John
  • 59
  • 1
  • 5