[英]Populating a list using FieldList, FormField, and populate_obj in WTForms, with adding items client-side
I have an application I am building using Python and Flask.我有一个正在使用 Python 和 Flask 构建的应用程序。 I have a form I am creating using WTForms which will allow the user to edit customer contact details, including a dynamic number of phone numbers.
我有一个使用 WTForms 创建的表单,它允许用户编辑客户联系方式,包括电话号码的动态号码。 When the form is submitted, I want to save the data from the form back into the
Customer
object using the form's populate_obj
function.提交表单后,我想使用表单的
populate_obj
function 将表单中的数据保存回Customer
object。
The code for the forms is as follows: 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')
I have the following javascript to add additional phone number fields on the client side:我有以下 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);
}
Everything seems to work as I expect as long as I don't add an additional phone number client-side.只要我不在客户端添加额外的电话号码,一切似乎都按我的预期工作。 However, if I do add another number client-side, when I call
populate_obj
I get the following exception:但是,如果我在客户端添加另一个号码,当我调用
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
I'm pretty sure it's because the phones
property (which is a list of PhoneNumber
objects) in the corresponding Customer
object doesn't have enough items in it to contain all the form data into the list.我很确定这是因为相应的
Customer
object 中的phones
属性(它是PhoneNumber
对象的列表)中没有足够的项目来将所有表单数据包含到列表中。
I've looked over the WTForms documentation to see if there is a way to assign a factory function or class to the FormField
so that it could create additional PhoneNumber
objects to add to customer.phones
when I call populate_obj
if there are more items in the form data than in the target object.我查看了 WTForms 文档,看看是否有办法将工厂 function 或 class 分配给
FormField
以便它可以创建额外的PhoneNumber
对象以在我调用populate_obj
时添加到customer.phones
形成数据比目标 object 中的数据。 However, so far as I can tell from the documentation there is no such option.但是,据我从文档中可以看出,没有这样的选项。
Anyone know the best way to do this?有人知道最好的方法吗?
Okay, I spend some time studying the WTForms source code on Github and figured it out.好的,我花了一些时间研究 Github 上的 WTForms 源代码并弄清楚了。 Pass a class or factory function to the
default
parameter of the FormField
instance, as follows:将 class 或工厂 function 传递给
FormField
实例的default
参数,如下所示:
phones = FieldList(FormField(PhoneNumberFormPart, default=PhoneNumber), min_entries=1)
It's actually pretty easy, although calling the parameter default
made it a little hard for me to find.这实际上很容易,尽管调用参数
default
让我很难找到。 Never would have even thought to look there...从来没有想过要看那里...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.