为什么 Flask-Admin 在尝试编辑模型时会抛出此异常?
Why is Flask-Admin throwing this exception when attempting to edit a model?
例外是 AttributeError: 'StringField' object has no attribute 'wrap_formdata'
,它似乎只发生在我从 Flask-Admin 仪表板创建或编辑以下两个模型之一时:
class Experiment(db.Model):
id = db.Column(db.Integer, primary_key=True)
property_id = db.Column(db.Integer,
db.ForeignKey('property.id'),
index=True,
nullable=False)
name = db.Column(db.String, nullable=False)
start_date = db.Column(db.DateTime, nullable=True)
end_date = db.Column(db.DateTime, nullable=True)
status = db.Column(db.String, nullable=False, default='Draft')
title = db.Column(db.String)
meta = db.Column(db.String)
seed_uri = db.Column(db.String)
targets = db.Column(postgresql.JSON)
variations_json = db.Column(postgresql.JSON)
variations = db.relationship('ExperimentVariation',
backref='experiment',
cascade='save-update, merge, delete')
def __repr__(self):
repr_fmt = '<Experiment {id}, {property_id} {name}>'
return repr_fmt.format(id=self.id,
property_id=self.property_id,
name=self.name)
class ExperimentVariation(db.Model):
id = db.Column(db.Integer, primary_key=True)
experiment_id = db.Column(db.Integer,
db.ForeignKey('experiment.id'),
index=True,
nullable=False)
name = db.Column(db.String, nullable=False)
title = db.Column(db.String)
meta = db.Column(db.String)
def __repr__(self):
repr_fmt = '<ExperimentVariation {id}, {experiment_id} {name}>'
return repr_fmt.format(id=self.id,
experiment_id=self.experiment_id,
name=self.name)
我想知道这些关系是否有些复杂。如果有帮助,我可以提供 Property
模型,但简而言之,该模型的大部分只是提供其他模型(包括实验模型)之间的关系。
快速 Google 似乎没有发现类似的问题。我很可能忽略了一些明显的东西,或者可能没有完全理解这种关系 API。
此外,为了清楚起见,我在下面包含了完整的回溯:
ERROR [ranksci] Exception on /admin/experiment/edit/ [GET]
Traceback (most recent call last):
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1988, in wsgi_app
response = self.full_dispatch_request()
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1641, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1544, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1625, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/base.py", line 69, in inner
return self._run_view(f, *args, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/base.py", line 368, in _run_view
return fn(self, *args, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/model/base.py", line 1969, in edit_view
form = self.edit_form(obj=model)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/model/base.py", line 1256, in edit_form
return self._edit_form_class(get_form_data(), obj=obj)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/wtforms/form.py", line 212, in __call__
return type.__call__(cls, *args, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/form/__init__.py", line 16, in __init__
super(BaseForm, self).__init__(formdata=formdata, obj=obj, prefix=prefix, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/wtforms/form.py", line 278, in __init__
self.process(formdata, obj, data=data, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/wtforms/form.py", line 119, in process
formdata = self.meta.wrap_formdata(self, formdata)
AttributeError: 'StringField' object has no attribute 'wrap_formdata'
我找到了这个问题的根本原因:Flask-Admin 构造了一个表单 class,默认情况下它从每个SQLA模型。这一切都很好,除非您的其中一列与 WTForm 的构造函数采用的参数之一相匹配,例如 meta
.
构造所述 classes 的逻辑是 here。我不完全确定正确的解决方法是什么,但是 Flask-Admin 似乎确实需要在技术上或社交上处理这种情况。例如,提前知道 meta
不应用作列名会很方便,因为会出现此问题。
值得指出的是,以下变量也被 WTForm 的 Form 构造函数接受,因此也应该避免使用:formdata
、obj
、prefix
和 data
.
这个问题的技术解决方案可能是构建一组这些变量名称,然后在上面的 Flask-Admin 代码中创建模型表单时显式检查它们。然后可以生成某种警告,或者可以更改 name
变量,使其带有前缀 _
或类似的东西。这不是一个完美的解决方案,因为 WTForms 当然可以在将来更改其 API。
例外是 AttributeError: 'StringField' object has no attribute 'wrap_formdata'
,它似乎只发生在我从 Flask-Admin 仪表板创建或编辑以下两个模型之一时:
class Experiment(db.Model):
id = db.Column(db.Integer, primary_key=True)
property_id = db.Column(db.Integer,
db.ForeignKey('property.id'),
index=True,
nullable=False)
name = db.Column(db.String, nullable=False)
start_date = db.Column(db.DateTime, nullable=True)
end_date = db.Column(db.DateTime, nullable=True)
status = db.Column(db.String, nullable=False, default='Draft')
title = db.Column(db.String)
meta = db.Column(db.String)
seed_uri = db.Column(db.String)
targets = db.Column(postgresql.JSON)
variations_json = db.Column(postgresql.JSON)
variations = db.relationship('ExperimentVariation',
backref='experiment',
cascade='save-update, merge, delete')
def __repr__(self):
repr_fmt = '<Experiment {id}, {property_id} {name}>'
return repr_fmt.format(id=self.id,
property_id=self.property_id,
name=self.name)
class ExperimentVariation(db.Model):
id = db.Column(db.Integer, primary_key=True)
experiment_id = db.Column(db.Integer,
db.ForeignKey('experiment.id'),
index=True,
nullable=False)
name = db.Column(db.String, nullable=False)
title = db.Column(db.String)
meta = db.Column(db.String)
def __repr__(self):
repr_fmt = '<ExperimentVariation {id}, {experiment_id} {name}>'
return repr_fmt.format(id=self.id,
experiment_id=self.experiment_id,
name=self.name)
我想知道这些关系是否有些复杂。如果有帮助,我可以提供 Property
模型,但简而言之,该模型的大部分只是提供其他模型(包括实验模型)之间的关系。
快速 Google 似乎没有发现类似的问题。我很可能忽略了一些明显的东西,或者可能没有完全理解这种关系 API。
此外,为了清楚起见,我在下面包含了完整的回溯:
ERROR [ranksci] Exception on /admin/experiment/edit/ [GET]
Traceback (most recent call last):
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1988, in wsgi_app
response = self.full_dispatch_request()
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1641, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1544, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask/app.py", line 1625, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/base.py", line 69, in inner
return self._run_view(f, *args, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/base.py", line 368, in _run_view
return fn(self, *args, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/model/base.py", line 1969, in edit_view
form = self.edit_form(obj=model)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/model/base.py", line 1256, in edit_form
return self._edit_form_class(get_form_data(), obj=obj)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/wtforms/form.py", line 212, in __call__
return type.__call__(cls, *args, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/flask_admin/form/__init__.py", line 16, in __init__
super(BaseForm, self).__init__(formdata=formdata, obj=obj, prefix=prefix, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/wtforms/form.py", line 278, in __init__
self.process(formdata, obj, data=data, **kwargs)
File "/Users/max/Documents/projects/ranksci-app/virtualenv/lib/python2.7/site-packages/wtforms/form.py", line 119, in process
formdata = self.meta.wrap_formdata(self, formdata)
AttributeError: 'StringField' object has no attribute 'wrap_formdata'
我找到了这个问题的根本原因:Flask-Admin 构造了一个表单 class,默认情况下它从每个SQLA模型。这一切都很好,除非您的其中一列与 WTForm 的构造函数采用的参数之一相匹配,例如 meta
.
构造所述 classes 的逻辑是 here。我不完全确定正确的解决方法是什么,但是 Flask-Admin 似乎确实需要在技术上或社交上处理这种情况。例如,提前知道 meta
不应用作列名会很方便,因为会出现此问题。
值得指出的是,以下变量也被 WTForm 的 Form 构造函数接受,因此也应该避免使用:formdata
、obj
、prefix
和 data
.
这个问题的技术解决方案可能是构建一组这些变量名称,然后在上面的 Flask-Admin 代码中创建模型表单时显式检查它们。然后可以生成某种警告,或者可以更改 name
变量,使其带有前缀 _
或类似的东西。这不是一个完美的解决方案,因为 WTForms 当然可以在将来更改其 API。