create dynamic fields in WTform in Flask
<p>I want to create different forms in Flask using WTForms and Jinja2. I make a call to mysql, which has the type of field. </p>

<p>So i.e. the table could be:</p>

<pre><code> form_id   |  type         |   key    |    options      | default_value
    1      |  TextField    |   title  |                 |      test1
    1      |  SelectField  |   gender |{'male','female'}|      
    2      |  TextAreaField|   text   |                 |   Hello, World!
</code></pre>

<p>Then I query on form_id. then I want to create a form with WTforms having the fields of the rows which are returned.</p>

<p>For a normal form I do:</p>

<pre><code>class MyForm(Form):

    title = TextField('test1', [validators.Length(min=4, max=25)])
    gender = SelectField('', choices=['male','female'])


def update_form(request):

     form = MyForm(request.form)

     if request.method == 'POST' and form.validate():
          title = form.title.data
          gender = form.gender.data

          #do some updates with data
          return .....
     else:
          return render_template('template.html',form)
          #here should be something like:
          #dict = query_mysql()
          #new_form = MyForm(dict);
          #render_template('template.html',new_form)
</code></pre>

<p>I think best would be to create an empty form and then add fields in a for-loop, however if a form is posted back how can I validate the form if I don't have it defined in a class? I do have the form_id in the form so I can generate it and then validate.</p>

<h1>Adding fields dynamically</h1>

<blockquote>
  <p>I think best would be to create an empty form and then add fields in a for-loop, however if a form is posted back how can I validate the form if I don't have it defined in a class?</p>
</blockquote>

<p>Add the fields to the <strong>form class</strong> using <code>setattr</code> before the form is instantiated:</p>

<pre><code>def update_form(request):
    table = query()

    class MyForm(Form):
        pass

    for row in table:
        setattr(MyForm, row.key, SomeField())

    form = MyForm(request.form)
</code></pre>

<p>However, I think your question is part of a bigger problem, which I have tried to address below.</p>

<h1>Mapping tables to forms</h1>

<p>Your table seem to map very nicely to the form itself. If you want to create forms dynamically from your tables, you could write the logic yourself. But when the range of fields and options to support grows, it can be a lot of work to maintain. If you are using <a href="http://www.sqlalchemy.org" rel="nofollow">SQLAlchemy</a>, you might want to take a look at <a href="https://wtforms-alchemy.readthedocs.io/" rel="nofollow">WTForms-Alchemy</a>. From its introduction:</p>

<blockquote>
  <p>Many times when building modern web apps with SQLAlchemy youâll have
  forms that map closely to models. For example, you might have a
  Article model, and you want to create a form that lets people post new
  article. In this case, it would be time-consuming to define the field
  types and basic validators in your form, because youâve already
  defined the fields in your model.</p>
  
  <p>WTForms-Alchemy provides a helper class that let you create a Form
  class from a SQLAlchemy model.</p>
</blockquote>

<p>The helper class is <code>ModelForm</code>, and in the style of your table, below is a Python 2/3 sample with WTForms-Alchemy. Install the package <code>wtforms-alchemy</code> first, which will pull in SQLAlchemy and WTForms as well.</p>

<pre><code>from __future__ import print_function
from __future__ import unicode_literals

import sqlalchemy as sa
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from wtforms_alchemy import ModelForm

engine = create_engine('sqlite:///:memory:')
Base = declarative_base(engine)
Session = sessionmaker(bind=engine)
session = Session()


class MyClass(Base):
    __tablename__ = 'mytable'

    id = sa.Column(sa.BigInteger, autoincrement=True, primary_key=True)
    title = sa.Column(sa.Unicode(5), nullable=False)
    gender = sa.Column(sa.Enum('male', 'female', name='gender'))
    text = sa.Column(sa.Text)


class MyForm(ModelForm):
    class Meta:
        model = MyClass


form = MyForm()

print('HTML\n====')
for field in form:
    print(field)
</code></pre>

<p>Running the above code prints:</p>

<pre><code>HTML
====
&lt;input id="title" name="title" required type="text" value=""&gt;
&lt;select id="gender" name="gender"&gt;&lt;option value="male"&gt;male&lt;/option&gt;&lt;option value="female"&gt;female&lt;/option&gt;&lt;/select&gt;
&lt;textarea id="text" name="text"&gt;&lt;/textarea&gt;
</code></pre>

<p>As you can see, WTForms-Alchemy did a whole lot with <code>MyForm</code>. The class is essentially this: </p>

<pre><code>class MyForm(Form):
    title = StringField(validators=[InputRequired(), Length(max=5)])
    gender = SelectField(choices=[('male', 'male'), ('female', 'female')])
    text = TextField()
</code></pre>

<p>The documentation for WTForms-Alchemy seems to be very comprehensive. I have not used it myself, but if I had a similar problem to solve I would definitely try it out.</p>

python flask wtforms