Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wtforms, generate fields in constructor

I need to generate my fields in the constructor of the form since the number of fields needed can vary. I think my current solution is the problem. I get an exception when I try to expand the form in my template saying

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

What is wrong with this code?

class DriverTemplateSchedueForm(Form):
    def __init__(self, per_day=30, **kwargs):
        self.per_day = per_day
        ages = model.Agency.query.all()
        ages = [(a.id, a.name) for a in ages]
        self.days = [[[]] * per_day] * 7
        for d in range(7):
            for i in range(per_day):
                lbl = 'item_' + str(d) + '_' + str(i)
                self.__dict__[lbl] = SelectField(lbl, choices=ages)
                self.days[d][i] = self.__dict__[lbl]
        for day in self.days:
            print(day)

        Form.__init__(self, **kwargs)
like image 938
chasep255 Avatar asked Jul 01 '15 12:07

chasep255


People also ask

What is form Validate_on_submit?

The validate_on_submit() method of the form returns True when the form was submitted and the data was accepted by all the field validators. In all other cases, validate_on_submit() returns False . The return value of this method effectively serves to determine whether the form needs to be rendered or processed.

What does flask-WTF stand for?

WTF stands for WT Forms which is intended to provide the interactive user interface for the user. The WTF is a built-in module of the flask which provides an alternative way of designing forms in the flask web applications.

What is StringField?

StringField (default field arguments)[source] This field is the base for most of the more complicated fields, and represents an <input type="text"> .


2 Answers

The fix

You need to add the fields to your class not your instance:

def driver_template_schedue_form(ages, per_day=30, **kwargs):
    """Dynamically creates a driver's schedule form"""

    # First we create the base form
    # Note that we are not adding any fields to it yet
    class DriverTemplateScheduleForm(Form):
        pass

    # Then we iterate over our ranges
    # and create a select field for each
    # item_{d}_{i} in the set, setting each field
    # *on our **class**.
    for d in range(7):
        for i in range(per_day):
            label = 'item_{:d}_{:d}'.format(d, i)
            field = SelectField(label, choices=ages)
            setattr(DriverTemplateScheduleForm, label, field)

    # Finally, we return the *instance* of the class
    # We could also use a dictionary comprehension and then use
    # `type` instead, if that seemed clearer.  That is:
    # type('DriverTemplateScheduleForm', Form, our_fields)(**kwargs)
    return DriverTemplateScheduleForm(**kwargs)

Why doesn't adding fields to self work?

WTForms uses meta-classes to register forms and fields together and preserve order. *Field instances are created unbound, added to the Form class' _unbound_fields attribute and are bound to the class instance when the class is being constructed by the meta-class.

By the time DriverTemplateScheduleForm.__init__ is run, _unbound_fields has already been populated. You can push your fields into self._unbound_fields and things would also just work, but that's making use of a private API and so may break later.

like image 121
Sean Vieira Avatar answered Oct 08 '22 11:10

Sean Vieira


The answer about meta-class is right, but in case if you really need this (like I do):

class SomeForm(Form):
    def __init__(self, *args, **kwargs):
        for name in kwargs.keys():
            if name.startswith('PREFIX_'):
                field = HiddenField()
                setattr(self, name, field)
                self._unbound_fields = self._unbound_fields + [[name, field]]
        super(SomeForm, self).__init__(*args, **kwargs)

Note that we're not modifying _unbound_fields, not to have this fields in form class next time.

like image 45
Victor Gavro Avatar answered Oct 08 '22 11:10

Victor Gavro