繁体   English   中英

在 WTForms 中使用 FieldList、FormField 和 populate_obj 填充列表,并在客户端添加项目

[英]Populating a list using FieldList, FormField, and populate_obj in WTForms, with adding items client-side

我有一个正在使用 Python 和 Flask 构建的应用程序。 我有一个使用 WTForms 创建的表单,它允许用户编辑客户联系方式,包括电话号码的动态号码。 提交表单后,我想使用表单的populate_obj function 将表单中的数据保存回Customer object。

forms的代码如下:

class PhoneNumberFormPart(FlaskForm):
    class Meta:
        csrf = False # Disable CSRF protection, it will confilct with protection on the parent form
    number = StringField("Phone Number", widget=Input('tel'))
    label = SelectField('Label', choices=(("Cell", "Cell"), ("Home", "Home"), ("Work", "Work")))
    preferred = BooleanField('Preferred', default=False)

class CustomerEditForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired()])
    name2 = StringField('Contact Person Name')
    isBusiness = BooleanField('This is a business client', default=False)
    phones = FieldList(FormField(PhoneNumberFormPart), min_entries=1)
    address = StringField("Address")
    city = StringField("City")
    state = StringField("State")
    zip = StringField("Zip Code")
    email = StringField("Email Address", widget=Input('email'), validators=[Email()])
    submit = SubmitField('Save Customer Details')

我有以下 javascript 在客户端添加额外的电话号码字段:

/// Called in forms using a FieldList to duplicate the last FieldList-item
function addFieldListItem(fieldList){
    let lastField = fieldList.lastElementChild.previousElementSibling; 
    // Last child is the "add phone" button added in the template, so the last field is the 2nd-to-last item
    let newField = lastField.cloneNode(true);

    let newInputs = newField.getElementsByTagName('input');
    Array.prototype.forEach.call(newInputs, function(input){
        // Increment the number that flask assigns to each field name
        input.name = input.name.replace(/(\d+)/, function(n){return ++n});
        input.id = input.id.replace(/(\d+)/, function(n){return ++n});
        // Clear the input values
        input.value = null;
        input.checked = false;
    });

    let newSelects = newField.getElementsByTagName('select');
    Array.prototype.forEach.call(newSelects, function(select){
        // Increment the number that flask assigns to each field name
        select.name = select.name.replace(/(\d+)/, function(n){return ++n});
        select.id = select.id.replace(/(\d+)/, function(n){return ++n});
    });

    let newLabels = newField.getElementsByTagName('label');
    Array.prototype.forEach.call(newLabels, function(label){
        // Increment the number that flask assigns to each field name
        label.htmlFor = label.htmlFor.replace(/(\d+)/, function(n){return ++n});
    });

    fieldList.insertBefore(newField, fieldList.lastElementChild);
}

只要我不在客户端添加额外的电话号码,一切似乎都按我的预期工作。 但是,如果我在客户端添加另一个号码,当我调用populate_obj时,会出现以下异常:

Traceback (most recent call last):
  File "c:\python\lib\site-packages\flask\app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File "c:\python\lib\site-packages\flask\app.py", line 2449, in wsgi_app
    response = self.handle_exception(e)
  File "c:\python\lib\site-packages\flask\app.py", line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "c:\python\lib\site-packages\flask\_compat.py", line 39, in reraise
    raise value
  File "c:\python\lib\site-packages\flask\app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "c:\python\lib\site-packages\flask\app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "c:\python\lib\site-packages\flask\app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "c:\python\lib\site-packages\flask\_compat.py", line 39, in reraise
    raise value
  File "c:\python\lib\site-packages\flask\app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File "c:\python\lib\site-packages\flask\app.py", line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "c:\python\lib\site-packages\flask_login\utils.py", line 261, in decorated_view
    return func(*args, **kwargs)
  File "C:\Users\Techris Design\Project Spiderman\spiderman\spiderman\views\customers.py", line 40, in customer_landing
    form.populate_obj(customer)
  File "c:\python\lib\site-packages\wtforms\form.py", line 96, in populate_obj
    field.populate_obj(obj, name)
  File "c:\python\lib\site-packages\wtforms\fields\core.py", line 962, in populate_obj
    field.populate_obj(fake_obj, 'data')
  File "c:\python\lib\site-packages\wtforms\fields\core.py", line 829, in populate_obj
    raise TypeError('populate_obj: cannot find a value to populate from the provided obj or input data/defaults')
TypeError: populate_obj: cannot find a value to populate from the provided obj or input data/defaults

我很确定这是因为相应的Customer object 中的phones属性(它是PhoneNumber对象的列表)中没有足够的项目来将所有表单数据包含到列表中。

我查看了 WTForms 文档,看看是否有办法将工厂 function 或 class 分配给FormField以便它可以创建额外的PhoneNumber对象以在我调用populate_obj时添加到customer.phones形成数据比目标 object 中的数据。 但是,据我从文档中可以看出,没有这样的选项。

有人知道最好的方法吗?

好的,我花了一些时间研究 Github 上的 WTForms 源代码并弄清楚了。 将 class 或工厂 function 传递给FormField实例的default参数,如下所示:

phones = FieldList(FormField(PhoneNumberFormPart, default=PhoneNumber), min_entries=1)

这实际上很容易,尽管调用参数default让我很难找到。 从来没有想过要看那里...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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