18

I am trying to generate a form in WTForms that has dynamic fields according to this documentation http://wtforms.simplecodes.com/docs/1.0.2/specific_problems.html#dynamic-form-composition

I have this subform class which allows users to pick items to purchase from a list:

class Item(Form):
    itmid = SelectField('Item ID')
    qty = IntegerField('Quantity')

class F(Form):
        pass

There will be more than one category of shopping items, so I would like to generate a dynamic select field based on what categories the user will choose:

fld = FieldList(FormField(Item))
fld.append_entry()

but I get the following error:

AttributeError: 'UnboundField' object has no attribute 'append_entry'

Am I doing something wrong, or is there no way to accomplish this in WTForms?

Soviut
  • 88,194
  • 49
  • 192
  • 260
ChiliConSql
  • 203
  • 1
  • 3
  • 7
  • Can you post more of the code around what you're doing with `fld`? Ideally, it would be an attribute of a Form class like in the example in the docs: http://wtforms.simplecodes.com/docs/1.0.2/fields.html#wtforms.fields.FieldList – aezell Oct 15 '12 at 19:47

6 Answers6

10

I ran into this issue tonight and ended up with this. I hope this helps future people.

RecipeForm.py

class RecipeForm(Form):
    category = SelectField('Category', choices=[], coerce=int)
    ...

views.py

@mod.route('/recipes/create', methods=['POST'])
def validateRecipe():
    categories = [(c.id, c.name) for c in g.user.categories.order_by(Category.name).all()]
    form = RecipeForm(request.form)
    form.category.choices = categories
    ...

@mod.route('/recipes/create', methods=['GET'])
def createRecipe():
    categories = [(c.id, c.name) for c in g.user.categories.order_by(Category.name).all()]
    form = RecipeForm(request.form)
    form.category.choices = categories
    return render_template('recipes/createRecipe.html', form=form)

I found this post helpful as well

Community
  • 1
  • 1
Matt Carrier
  • 5,602
  • 6
  • 26
  • 30
7
class BaseForm(Form):
    @classmethod
    def append_field(cls, name, field):
        setattr(cls, name, field)
        return cls

from forms import TestForm
form = TestForm.append_field("do_you_want_fries_with_that",BooleanField('fries'))(obj=db_populate_object)

I use the extended class BaseForm for all my forms and have a convenient append_field function on class.

Returns the class with the field appended, since instances (of Form fields) can't append fields.

dza
  • 1,478
  • 2
  • 13
  • 24
  • This modifies the form class *in place*. Do not use this for classes that are shared. Consider creating a new subclass instead: `return type(cls.__name__, (cls,), {name: field})`. – Martijn Pieters Aug 08 '18 at 19:12
6

Posting without writing full code or testing the code, but maybe it will give you some ideas. Also this could maybe only help with the filling the needed data.

You need to fill choices for SelectField to be able to see the data and be able to select it. Where you fill that? Initial fill should be in the form definition, but if you like dynamic one, I would suggest to modify it in the place where you creating this form for showing to the user. Like the view where you do some form = YourForm() and then passing it to the template.

How to fill form's select field with choices? You must have list of tuples and then something like this:

form.category_select.choices = [(key, categories[key]) for key in categories]
form.category_select.choices.insert(0, ("", "Some default value..."))

categories here must be dictionary containing your categories in format like {1:'One', 2:'Two',...}

So if you will assign something to choices when defining the form it will have that data from the beginning, and where you need to have user's categories, just overwrite it in the view.

Hope that will give you some ideas and you can move forward :)

Ignas Butėnas
  • 6,061
  • 5
  • 32
  • 47
  • 1
    Thanks @ignas-b, but this only fills in the Select field with values, it does not solve the error I'm getting. – ChiliConSql Oct 12 '12 at 14:03
  • Yeah... when I posted answer, I re-read question and answer and added "Also this could maybe only help with the filling the needed data."... :) Oh well :) – Ignas Butėnas Oct 14 '12 at 21:42
1

have you tried calling append_entry() on the form instance instead of the FieldList definition?

class F(Form)
  fld = FieldList(SelectField(Item))

form = F()
form.fld.append_entry()
John Ong
  • 31
  • 2
1

This is how i got it to work.

class MyForm(FlaskForm):
    mylist = SelectField('Select Field', choices=[])

@app.route("/test", methods=['GET', 'POST']
def testview():
    form = MyForm()
    form.mylist.choices = [(str(i), i) for i in range(9)]

Strangely this whole thing stops working for me if i use coerce=int. I am myself a flask beginner, so i am not really sure why coerce=int causes issue.

Rohit
  • 3,659
  • 3
  • 35
  • 57
-1

WTForms Documentation : class wtforms.fields.SelectField

Select fields with dynamic choice values:

class UserDetails(Form):
    group_id = SelectField(u'Group', coerce=int)

def edit_user(request, id):
    user = User.query.get(id)
    form = UserDetails(request.POST, obj=user)
    form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
lfzyx
  • 1
  • 1