5

I understand the SelectField method in WTForms takes can argument choices which has the form...

choices=[("value1", "display of value 1"), ("value2", "display of value 2")]

I need to populate my choices based on a call to the database. I'm using neo4j as my backend, so I can't use modelforms or the other built-in solutions for populating data in a form.

def get_list_of_things():
    query = 'start n = node:things("*:*") return ID(n), n.display_name;'
    t = cypher.execute(cdb, query)[0]
    for x in t:
        x[0] = str(x[0])
        x[1] = str(x[1])
    things = list(tuple(x) for x in t)
    return things

class SelectAThing(Form):
    thingID = SelectField('Thing name', choices=get_list_of_things())

Running choices = get_list_of_things() does produce a valid list of choices, great, and this basically works.

However, it doesn't ever seem to update the list of things even when the database does and I return to that form later. If I add things to the db and return, I still see the first list of things.

Mittenchops
  • 18,633
  • 33
  • 128
  • 246

3 Answers3

7

No dummy, just don't put it in the class. Put it in the view code.

@app.route('/route')
def routename()
    form = SelectAThing()
    form.orgid.choices=get_joinable_orgs()

I found this tricky because I didn't realize I could assign to form like it was a regular python object after initializing it in the view.

Mittenchops
  • 18,633
  • 33
  • 128
  • 246
7

Yup - you got it. WTForms is a little unintuitive that way.

By the way, if you're pulling choices out of a SQLAlchemy database (and you're using Flask), check out the QuerySelectField addon:

http://wtforms.simplecodes.com/docs/0.6.1/ext.html#module-wtforms.ext.sqlalchemy

from wtforms.ext.sqlalchemy.fields import QuerySelectField

def all_employees():
  return Employee.query

class BugReportForm(Form):
  description = TextField(u"Notes")
  # The get_label will show the "name" attribute of the Employee model
  assign_to = QuerySelectField(u'Assign to',
                           get_label=u"name",
                           query_factory=all_employees)

That'll give you a Select field with the names of everybody.

Bonus: when you access BugReportForm.assign_to.data in the view code, it'll return the Employee object (not the id). It's handy.

Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
Rachel Sanders
  • 5,734
  • 1
  • 27
  • 36
0

I had the same problem, I tried QuerySelectField but because it is not "select input" and it is a ul element, it can't accept events like onchange and hard to get the value with JavaScript, so I have to keep the selectField field, and since it's not a custom path and this is modal View , I updated kawargs['form'].column.choices inside the render function and turn of the validation for selectField to accept updated streets without reloading the application

form_extra_fields = {
    'streetname': SelectField(
        'streetname',
        coerce=str,
        choices=([street.streetname for street in db.session.query(StreetsMetadata).all()]),
        render_kw={'onchange': "myFunction()"},
        validate_choice=False
        ),
    }

def render(self, template, **kwargs):
    """
    using extra js in render method allow use
    url_for that itself requires an app context
    """
    # solved By Python King
    if 'form' in kwargs and 'streetname' in kwargs['form'] and 'choices' in vars(kwargs['form'].streetname):
        kwargs['form'].streetname.choices = [street.streetname for street in db.session.query(StreetsMetadata).all()]
    self.extra_js = [url_for("static", filename="admin/js/users.js")]
    response = render_miror(self, template, **kwargs)
    return response
Mahmoud Magdy
  • 662
  • 10
  • 13