简体   繁体   中英

How do I bind an flask-wtform UnboundField?

I am creating an app to post products to a marketplace. Each product category on this marketplace has a different form for product attributes, for example if it's electronic there are fields for voltage, model number... I get this information via json through the marketplace server. First the user has to write the name of the product and it's main category and the marketplace server predicts their own category and returns to me it's attributes. Here's an example of the response:

{
"id": "MODEL",
"name": "Modelo",
"tags": {
  "hidden": true
},
{
"id": "PACKAGE_LENGTH",
"name": "Comprimento da embalagem",
"tags": {
  "hidden": true,
  "read_only": true,
  "variation_attribute": true
},
"hierarchy": "ITEM",
"relevance": 2,
"value_type": "number_unit",
"value_max_length": 255,
"allowed_units": [
  {
    "id": "km",
    "name": "km"
  },
  {
    "id": "polegadas",
    "name": "polegadas"
  },
  {
    "id": "ft",
    "name": "ft"
  },
  {
    "id": "mm",
    "name": "mm"
  },
  {
    "id": "m",
    "name": "m"
  },
  {
    "id": "\"",
    "name": "\""
  },
  {
    "id": "in",
    "name": "in"
  },
  {
    "id": "cm",
    "name": "cm"
  }
],
"default_unit": "cm",
"attribute_group_id": "OTHERS",
"attribute_group_name": "Outros"
}

So far so good.

Now I need to create a dynamic form to be able to add/edit those fields because each category has a different type of attribute, and different amount. Each field needs to have an ID, a type (could be string only, or numbers only) an label, and a max length and if its of value_type number_unit needs a SelectField with the given units.

I'm getting this information in a view, and at the same view I create a subclass of a FlaskForm, where I create a list of fields based on the type of the attribute.

   class DynamicForm(PostProductForm):
        loaded_attr = json.loads(meli.get(f"/categories/{category}/attributes").content.decode('utf-8'))
        attribute_fields = []
        for attribute in loaded_attr:
            tags = attribute.get('tags')
            if not tags.get('read_only'):
                if attribute.get('value_type') == 'number_unit':
                    attribute_fields.append(IntegerField(
                        attribute['name'], validators=[Length(min=0, max=attribute['value_max_length'])]))
                    allowed_units = [(unit['id'], unit['name']) for unit in attribute['allowed_units']]
                    attribute_fields.append(SelectField('Unidade', choices=allowed_units))

                elif attribute['value_type'] == 'string':
                    attribute_fields.append(StringField(
                        attribute['name'], validators=[Length(min=0, max=attribute['value_max_length'])]))

                elif attribute['value_type'] == 'number':
                    attribute_fields.append(IntegerField(
                        attribute['name'],
                        validators=[Length(min=0, max=attribute['value_max_length'])]))
                elif attribute['value_type'] == 'boolean':
                    attribute_fields.append(BooleanField(attribute['name']))

But this creates a bunch of UnboundFields. If I try to render only each field repr it shows me this:

<UnboundField(StringField, ('SKU ',), {'validators': [<wtforms.validators.Length object at 0x04DFBEF0>]})>

If I try to render the field it gives me an exception:

TypeError: 'UnboundField' object is not callable

I am looping through the list inside the template.

    {% for attribute in form.attribute_fields %}
    <div class="form-group blocked">
        {{ attribute() }}
    </div>
    {% endfor %}

How do I bind an UnboundField?

To create a dynamic form with wtforms use the following template

def DynamicForm(*args, **kwargs):
    class StaticForm(FlaskForm):
        pass

    if args[0] == True:
        StaticForm.class_attrib1 = StringField(...)
    else:
        StaticForm.class_attrib2 = StringField(...)

    return StaticForm()

This establishes a StaticForm within the local scope of the function, attaches all of the relevant items based on programming logic from the function arguments and returns an instantiated form:

form = DynamicForm(True)
# form has attribute: class_attrib1

This is explained in the docs somewhere, but I cant find the link right now

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM