14

I have a webapp that allows users to create their own fields to be rendered in a form later on.

I have a model Formfield like so:

class Formfield(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    form_id = db.Column(db.Integer, db.ForeignKey('formbooking.id'))
    label = db.Column(db.String(80))
    placeholder_text = db.Column(db.String(80))
    help_text = db.Column(db.String(500))
    box_checked = db.Column(db.Boolean, nullable = True, default = False)
    options = db.Column(db.String) # JSON goes here?
    answer = db.Column(db.String)
    required = db.Column(db.Boolean, nullable = False, default = False)
    order = db.Column(db.Integer)
    fieldtype = db.Column(db.Integer) 

that I use to represent a field, whatever the kind (checkbox, input, more in the future).

As you can see, every field has a FK to a form_id.

I’m trying to generate a dynamic form for a given form_id. The catch is that I need to determine the type of field to render for every Formfield. So I also need to process the fieldtype at some point.

I guess a solution would be to somehow pass the form_id to a function in my Form class.

I have no clue how to do it or where to look for a solution.

Any help would be very appreciated!

1 Answer 1

20

I think I managed to create dynamic forms with the idea from here https://groups.google.com/forum/#!topic/wtforms/cJl3aqzZieA

you have to create a dynamic form at view function, fetch the form field you want to get, and iterate every field to construct this form object. I used for fieldtypes simple text instead of integer values. Since it seems easy to read at code level.

class FormView(MethodView):
    def get(self):
        class DynamicForm(wtforms.Form): pass

        dform = main.models.Form.objects.get(name="name2")
        name = dform.name
        for f in dform.fields:
            print f.label
            setattr(DynamicForm , f.label, self.getField(f))

        d = DynamicForm() # Dont forget to instantiate your new form before rendering
        for field in d:
            print field # you can see html output of fields

        return render_template("customform.html", form=d)

    def getField(self, field): 
        if field.fieldtype == "text": 
            return TextField(field.label)
        if field.fieldtype == "password":
            return PasswordField(field.label)
        # can extend if clauses at every new fieldtype

for a simple form render jinja template 'forms.html'

{% macro render(form) -%}
<fieldset>
{% for field in form %}
{% if field.type in ['CSRFTokenField', 'HiddenField'] %}
  {{ field() }}
{% else %}
  <div class="form-group {% if field.errors %}error{% endif %}">
    {{ field.label }}
    <div class="input">
      {% if field.name == "body" %}
        {{ field(rows=10, cols=40) }}
      {% else %}
        {{ field() }}
      {% endif %}
      {% if field.errors or field.help_text %}
        <span class="help-inline">
        {% if field.errors %}
          {{ field.errors|join(' ') }}
        {% else %}
          {{ field.help_text }}
        {% endif %}
        </span>
      {% endif %}
    </div>
  </div>
{% endif %}
{% endfor %}
</fieldset>
{% endmacro %}

and customform.html is like this

{% extends "base.html" %}
{% import "forms.html" as forms %}

{% block content %}
{{ forms.render(form) }}
{% endblock %}
Sign up to request clarification or add additional context in comments.

5 Comments

thanks! For reference, see dynamic form composition in the wtforms docs.
Beauty, instantiating the form class object solved my problem.
@Dailyrox dead link.
@roy wtforms.simplecodes.com/docs/1.0.1/… here is the correct link
For those looking for an updated link to this documentation: wtforms.readthedocs.io/en/2.3.x/specific_problems/…

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.