1

So I have this flask app I'm making and I need some help with a variable access.

Most of the time, when you define a form in flask, you'll do the following :

class MyForm(Form):
    my_field = StringField('I'm a field')
    my_submit = SubmitField('Go!')

And when the time comes where you need the form, you'll declare an instance of that class with form = MyForm()

Up to here, it's all good, However :

If you want say, a SelectField (Dropdown) where the choices depend on the answers of a previous form, you need to be able to give the new form those choices. This is what I'm trying to achieve, but I can't get a variable to keep its contents.

Here is my Form code (Above the page code):

class DataMappingForm(Form):

    dm_choices = #I need this array !

    DMpatient_id = SelectField(u'Select Patient ID Column', 
            choices=dm_choices, validators=[Required()])
    ...

Here is my Page code :

@app.route('/upload', methods=['GET','POST'])
def upload():
    uform = SomeOtherForm()
    if uform.is_submitted() and uform.data['Usubmit']:
        #Do stuff from previous form
        # and declare array_of_choices
    dmform = DataMappingForm() #Needs array_of_choices to work
    ...

What I've tried so far :

  • session['dm_choices'] gives me a working outside of request context error
  • global variables, get reset for some reason
  • overloading the __init__ of Form by adding the array but i can't access it in the parts above the __init__ function.

I should mention, this all needs to be on the same page.

Is there a way to pass this array_of_choices to my DataMappingForm class ?

EDIT This is what it looked like when I trid the __init__ overload:

class DataMappingForm(Form):
     def __init__(self, dm_choices, *args, **kwargs):
         self.dm_choices = dm_choices
         Form.__init__(self, *args, **kwargs)

     DMpatient_id = SelectField(u'Select Patient ID Column', 
             choices=dm_choices, validators=[Required()])
#I've tried putting it above or below, I get 'dm_choices is not defined'
8
  • So you need the form to be dynamic without a page reload? Flask only handles server side code, for client side dynamics you need javascript. Commented Jul 5, 2016 at 13:36
  • No, I'm using the page reload already, what I mean is that the second form appears once you've referenced the first one, on the same page (after reload) Commented Jul 5, 2016 at 13:38
  • Then the approach with session and overloading __init__ should work. Can you give concrete code? Here is some documentation on dynamic forms. Commented Jul 5, 2016 at 13:39
  • "global variables, get reset for some reason" The reason is that each request is unique and can be handled by separate processes/threads. It would be really bad if a value set during the course of processing my request affected a subsequent request made by someone else. Commented Jul 5, 2016 at 13:40
  • Possible duplicate of Generate a dynamic form using flask-wtf and sqlalchemy Commented Jul 5, 2016 at 13:41

2 Answers 2

1

I've Got it ! Thanks to @synonym for pointing me in the right direction with your last link.

All you need to do is declare a function in which the class is defined. You then pass the variable to the function, and it will be accessible within the class.

Finally, make the function return the form object.

Example :

def makeMyForm(myArray):
    def class MyForm(Form):
        my_select_field = SelectField(u'I'm a select field', choices=myArray)
        my_submit = SubmitField(u'Go!')
    return MyForm()

And to make the form, you use :

form = makeMyForm(theArrayYouWant)

And Voilà !

Note : As I've had the problem before, I'll mention that the Array is composed of tuples :

myArray = [('value','What you see'),('value2','What you see again')]
Sign up to request clarification or add additional context in comments.

Comments

0

If you want to dynamically change the choices of a SelectField the following should work:

class DataMappingForm(Form):

    def __init__(self, choices)
        self.DMpatient_id.choices = choices

    DMpatient_id = SelectField(u'Select Patient ID Column') #note that choices is absent

If you want fully dynamic fields you can create the class dynamically in a function. From the WTForms Documentation:

def my_view():
    class F(MyBaseForm):
        pass

    F.username = StringField('username')
    for name in iterate_some_model_dynamically():
        setattr(F, name, StringField(name.title()))

    form = F(request.POST, ...)
    # do view stuff

In that case you can customize the form as much as you want. Of course in the case you only want to customize the choices the first approach should be enough.

3 Comments

Oh so that was it ! the part I was missing was self.DMpatient_id.choices = choices. Thanks for your help. The way I just posted also works btw. Anything wrong in using it ?
I think as long as it works your way is good. "Normally" one would not create classes dynamically, maybe that's why in that case they added a possibility to modify the choices later.
All right, I'll keep it in mind. Thanks for the help !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.