我有一个数据模型,该模型的列取决于其他列的值,按照this页上的说明,我创建了一个上下文相关函数,该函数用于确定该特定列的值。创作,像这样:
def get_column_value_from_context(context):
# Instructions to produce value
return value
class MyModel(db.Model):
id = db.Column(db.Integer,
primary_key=True)
my_column = db.Column(db.String(64),
nullable=False,
default=get_column_value_from_context)
name = db.Column(db.String(32),
nullable=False,
unique=True,
index=True)
title = db.Column(db.String(128),
nullable=False)
description = db.Column(db.String(256),
nullable=False)
这种方法相当不错,我可以从命令行或使用脚本创建行而不会出现问题。
我还使用Flask-Admin向应用程序中添加了ModelView
:
class MyModelView(ModelView):
can_view_details = True
can_set_page_size = True
can_export = True
admin.add_view(MyModelView(MyModel, db.session))
这也很不错,直到我单击 list 视图中的 Create 按钮。我收到此错误:
AttributeError:“ NoneType”对象没有属性“ get_current_parameters”
由于create_model
中ModelView
处理程序的实现是this:
def create_model(self, form):
"""
Create model from form.
:param form:
Form instance
"""
try:
model = self.model()
form.populate_obj(model)
self.session.add(model)
self._on_model_change(form, model, True)
self.session.commit()
except Exception as ex:
if not self.handle_view_exception(ex):
flash(gettext('Failed to create record. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create record.')
self.session.rollback()
return False
else:
self.after_model_change(form, model, True)
return model
,并且在实例化模型时这里没有context
。因此,我创建了一个自定义视图,可以在其中重新定义创建处理程序中的模型实例:
class CustomSQLAView(ModelView):
def __init__(self, *args, **kwargs):
super(CustomSQLAView, self).__init__(*args, **kwargs)
def create_model(self, form):
"""
Create model from form.
:param form:
Form instance
"""
try:
model = self.get_populated_model(form)
self.session.add(model)
self._on_model_change(form, model, True)
self.session.commit()
except Exception as ex:
if not self.handle_view_exception(ex):
flash(gettext('Failed to create record. %(error)s', error=str(ex)), 'error')
log.exception('Failed to create record.')
self.session.rollback()
return False
else:
self.after_model_change(form, model, True)
return model
def get_populated_model(self, form):
model = self.model()
form.populate_obj(model)
return model
现在,我可以重新定义get_populated_model
方法,以通常的方式实例化模型:
class MyModelView(CustomSQLAView):
can_view_details = True
can_set_page_size = True
can_export = True
def get_populated_model(self, form):
model = self.model(
name=form.name.data,
title=form.title.data,
description=form.description.data,
)
return model
尽管此方法有效,但我怀疑它会破坏某些内容。 Flask-Admin具有populate_obj
表单和字段方法的几种实现,因此,我想确保所有内容的安全。
执行此操作的正确方法是什么?
答案 0 :(得分:0)
因此,有几个因素会影响这个问题。
但是首先,一个功能齐全的小型应用程序来说明:
from flask_admin.contrib.sqla import ModelView
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask import Flask
app = Flask(__name__)
db = SQLAlchemy(app)
admin = Admin(app)
app.secret_key = 'arstartartsar'
def get_column_value_from_context(context):
print('getting value from context...')
if context.isinsert:
return 'its an insert!'
else:
return 'aww, its not an insert'
class MyModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
my_column = db.Column(db.String(64), nullable=False, default=get_column_value_from_context)
name = db.Column(db.String(32), nullable=False, unique=True, index=True)
class MyModelView(ModelView):
form_excluded_columns = ['my_column']
db.create_all()
admin.add_view(MyModelView(MyModel, db.session))
print('1')
x = MyModel(name='hey')
print('2')
db.session.add(x)
print('3')
db.session.commit()
print('4')
启动此应用时,打印的内容是:
1
2
3
getting value from context...
4
因此,仅提交后,get_column_value_from_context
函数才会启动。在创建视图中“创建”新模型时,您还没有上下文,因为您尚未向数据库提交任何内容。在将实例提交到数据库时,只有上下文可以设置默认值!因此,在flask admin的源代码中会发生这种情况:
if getattr(default, 'is_callable', False):
value = lambda: default.arg(None) # noqa: E731
他们检查您指定的默认值是否是一个函数,如果是,则在没有上下文的情况下调用它(因为还没有上下文!)。
根据您的目标,您可以选择几种方法来克服此问题:
1)仅将my_column
的值添加到数据库中时:
只需忽略表单中的字段,然后将其添加到数据库中即可确定。
class MyModelView(ModelView):
form_excluded_columns = ['my_column']
2)仅在将其添加到数据库时才进行计算,但之后可以对其进行编辑:
class MyModelView(ModelView):
form_edit_rules = ['name', 'my_column']
form_create_rules = ['name']