带有参数的 Flask-admin 批处理操作通过弹出模式 window
Flask-admin batch action with argument via pop-up modal window
有没有什么方法可以从 Flask 函数启动弹出窗口window?
我有一个 flask-admin
+ flask-sqlalchemy
应用程序。数据库中的 table 包含一个具有某些值的字段 foo
。我有一个 UserAdmin
视图,我正在尝试创建一个带有一些外部参数的 batch action。
即我想:
- select 我的数据库中的几个元素和
- 用一个新的用户定义值和
替换每个元素的旧foo
值
- 我想要接收这个新值的方式是模态 window。
所以模型是:
class User(db.Model):
# some code
foo = Column(Integer)
def change_foo(self, new_foo):
self.foo = int(new_foo)
db.session.commit()
return True
class UserAdmin(sqla.ModelView):
# some code
@action('change_foo', 'Change foo', 'Are you sure you want to change foo for selected users?')
def action_change_foo(self, ids):
query = tools.get_query_for_ids(self.get_query(), self.model, ids)
count = 0
# TODO: pop-up modal window here to get new_foo_value
# new_foo_value = ???
for user in query.all():
if user.change_foo(new_foo_value):
count += 1
flash(ngettext('Foo was successfully changed.',
'%(count)s foos were successfully changed.',
count, count=count))
except Exception as e:
flash(ngettext('Failed to change foo for selected users. %(error)s', error=str(e)), 'error')
我承认整个方法不是最优的,所以我很高兴被告知更好的方法。
有一些相关问题:«Batch Editing in Flask-Admin» (yet unanswered) and «Flask-Admin batch action with form»(使用 WTF 表单的一些解决方法)。
这是实现此目的的一种方法。我已将完整的独立示例放在 Github、flask-admin-modal.
上
2018 年 5 月 28 日更新。Github 项目已被另一个用户增强,可以很好地处理表单验证。
在此示例中,SQLite 数据库模型是一个具有名称(字符串)和成本(整数)属性的项目,我们将更新 Flask-Admin 列表视图中选定行的成本值。请注意,当 Flask 应用程序启动时,数据库会填充随机数据。
这是模型:
class Project(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
cost = db.Column(db.Integer(), nullable=False)
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return "Name: {name}; Cost : {cost}".format(name=self.name, cost=self.cost)
定义一个带有接受新成本的整数成本字段的表单。此表单还有一个隐藏字段,用于跟踪所选行的 ID。
class ChangeForm(Form):
ids = HiddenField()
cost = IntegerField(validators=[InputRequired()])
覆盖项目视图模型的列表模板。我们这样做是为了在 {% block body %}
中注入一个 Bootstrap 模态形式,id 为 changeModal
,确保我们首先调用 {{ super() }}
。
我们还添加了一个 jQuery 文档就绪函数,如果模板变量 (change_modal
) 的计算结果为真,该函数将显示模态形式。模态主体中使用相同的变量来显示 change_form
。我们使用 Flask-Admin lib 宏 render_form 以 Bootstrap 样式呈现表单。
注意 render_form
中 action 参数的值 - 这是我们在项目视图中定义的路由,我们可以在其中处理表单数据。另请注意,"Close" 按钮已替换为 link,但样式仍为按钮。 link 是发起操作的原始 url(包括页面和过滤器详细信息)。
{% extends 'admin/model/list.html' %}
{% block body %}
{{ super() }}
<div class="modal fade" tabindex="-1" role="dialog" id="changeModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<a href="{{ url }}" class="close" aria-label="Close"><span aria-hidden="true">×</span></a>
<h4 class="modal-title">Change Project Costs</h4>
</div>
<div class="modal-body">
{% if change_modal %}
{{ lib.render_form(change_form, action=url_for('project.update_view', url=url)) }}
{% endif %}
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endblock body %}
{% block tail %}
{{ super() }}
<script>
{% if change_modal %}
$(document).ready(function(){
$("#changeModal").modal('show');
});
{% endif %}
</script>
{% endblock tail %}
项目视图 class 需要修改批处理方法的行为并定义几个接受 POST 请求的路由。
@action('change_cost', 'Change Cost', 'Are you sure you want to change Cost for selected projects?')
def action_change_cost(self, ids):
url = get_redirect_target() or self.get_url('.index_view')
return redirect(url, code=307)
批处理操作获取发布操作的 url 而不是直接处理 ID,此 url 将包含任何页码和过滤器详细信息。然后它使用 307 重定向回列表视图。这确保了所选行的 ID 在主体中以及它是一个 POST 请求的事实。
定义一个POST路由来处理这个重定向,从请求体中获取ids和url,实例a ChangeForm
,设置隐藏的ids
表单域到 id 的编码列表。将 url
、change_form
和 change_model
变量添加到模板参数,然后再次呈现列表视图 - 这次模态弹出窗体将显示在视图中。
@expose('/', methods=['POST'])
def index(self):
if request.method == 'POST':
url = get_redirect_target() or self.get_url('.index_view')
ids = request.form.getlist('rowid')
joined_ids = ','.join(ids)
encoded_ids = base64.b64encode(joined_ids)
change_form = ChangeForm()
change_form.ids.data = encoded_ids
self._template_args['url'] = url
self._template_args['change_form'] = change_form
self._template_args['change_modal'] = True
return self.index_view()
定义一个POST 路由来处理模态表单的数据。这是标准 form/database 处理,完成后重定向回启动操作的原始 url。
@expose('/update/', methods=['POST'])
def update_view(self):
if request.method == 'POST':
url = get_redirect_target() or self.get_url('.index_view')
change_form = ChangeForm(request.form)
if change_form.validate():
decoded_ids = base64.b64decode(change_form.ids.data)
ids = decoded_ids.split(',')
cost = change_form.cost.data
_update_mappings = [{'id': rowid, 'cost': cost} for rowid in ids]
db.session.bulk_update_mappings(Project, _update_mappings)
db.session.commit()
return redirect(url)
else:
# Form didn't validate
# todo need to display the error message in the pop-up
print change_form.errors
return redirect(url, code=307)
有没有什么方法可以从 Flask 函数启动弹出窗口window?
我有一个 flask-admin
+ flask-sqlalchemy
应用程序。数据库中的 table 包含一个具有某些值的字段 foo
。我有一个 UserAdmin
视图,我正在尝试创建一个带有一些外部参数的 batch action。
即我想:
- select 我的数据库中的几个元素和
- 用一个新的用户定义值和 替换每个元素的旧
- 我想要接收这个新值的方式是模态 window。
foo
值
所以模型是:
class User(db.Model):
# some code
foo = Column(Integer)
def change_foo(self, new_foo):
self.foo = int(new_foo)
db.session.commit()
return True
class UserAdmin(sqla.ModelView):
# some code
@action('change_foo', 'Change foo', 'Are you sure you want to change foo for selected users?')
def action_change_foo(self, ids):
query = tools.get_query_for_ids(self.get_query(), self.model, ids)
count = 0
# TODO: pop-up modal window here to get new_foo_value
# new_foo_value = ???
for user in query.all():
if user.change_foo(new_foo_value):
count += 1
flash(ngettext('Foo was successfully changed.',
'%(count)s foos were successfully changed.',
count, count=count))
except Exception as e:
flash(ngettext('Failed to change foo for selected users. %(error)s', error=str(e)), 'error')
我承认整个方法不是最优的,所以我很高兴被告知更好的方法。
有一些相关问题:«Batch Editing in Flask-Admin» (yet unanswered) and «Flask-Admin batch action with form»(使用 WTF 表单的一些解决方法)。
这是实现此目的的一种方法。我已将完整的独立示例放在 Github、flask-admin-modal.
上2018 年 5 月 28 日更新。Github 项目已被另一个用户增强,可以很好地处理表单验证。
在此示例中,SQLite 数据库模型是一个具有名称(字符串)和成本(整数)属性的项目,我们将更新 Flask-Admin 列表视图中选定行的成本值。请注意,当 Flask 应用程序启动时,数据库会填充随机数据。
这是模型:
class Project(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
cost = db.Column(db.Integer(), nullable=False)
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return "Name: {name}; Cost : {cost}".format(name=self.name, cost=self.cost)
定义一个带有接受新成本的整数成本字段的表单。此表单还有一个隐藏字段,用于跟踪所选行的 ID。
class ChangeForm(Form):
ids = HiddenField()
cost = IntegerField(validators=[InputRequired()])
覆盖项目视图模型的列表模板。我们这样做是为了在 {% block body %}
中注入一个 Bootstrap 模态形式,id 为 changeModal
,确保我们首先调用 {{ super() }}
。
我们还添加了一个 jQuery 文档就绪函数,如果模板变量 (change_modal
) 的计算结果为真,该函数将显示模态形式。模态主体中使用相同的变量来显示 change_form
。我们使用 Flask-Admin lib 宏 render_form 以 Bootstrap 样式呈现表单。
注意 render_form
中 action 参数的值 - 这是我们在项目视图中定义的路由,我们可以在其中处理表单数据。另请注意,"Close" 按钮已替换为 link,但样式仍为按钮。 link 是发起操作的原始 url(包括页面和过滤器详细信息)。
{% extends 'admin/model/list.html' %}
{% block body %}
{{ super() }}
<div class="modal fade" tabindex="-1" role="dialog" id="changeModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<a href="{{ url }}" class="close" aria-label="Close"><span aria-hidden="true">×</span></a>
<h4 class="modal-title">Change Project Costs</h4>
</div>
<div class="modal-body">
{% if change_modal %}
{{ lib.render_form(change_form, action=url_for('project.update_view', url=url)) }}
{% endif %}
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endblock body %}
{% block tail %}
{{ super() }}
<script>
{% if change_modal %}
$(document).ready(function(){
$("#changeModal").modal('show');
});
{% endif %}
</script>
{% endblock tail %}
项目视图 class 需要修改批处理方法的行为并定义几个接受 POST 请求的路由。
@action('change_cost', 'Change Cost', 'Are you sure you want to change Cost for selected projects?')
def action_change_cost(self, ids):
url = get_redirect_target() or self.get_url('.index_view')
return redirect(url, code=307)
批处理操作获取发布操作的 url 而不是直接处理 ID,此 url 将包含任何页码和过滤器详细信息。然后它使用 307 重定向回列表视图。这确保了所选行的 ID 在主体中以及它是一个 POST 请求的事实。
定义一个POST路由来处理这个重定向,从请求体中获取ids和url,实例a ChangeForm
,设置隐藏的ids
表单域到 id 的编码列表。将 url
、change_form
和 change_model
变量添加到模板参数,然后再次呈现列表视图 - 这次模态弹出窗体将显示在视图中。
@expose('/', methods=['POST'])
def index(self):
if request.method == 'POST':
url = get_redirect_target() or self.get_url('.index_view')
ids = request.form.getlist('rowid')
joined_ids = ','.join(ids)
encoded_ids = base64.b64encode(joined_ids)
change_form = ChangeForm()
change_form.ids.data = encoded_ids
self._template_args['url'] = url
self._template_args['change_form'] = change_form
self._template_args['change_modal'] = True
return self.index_view()
定义一个POST 路由来处理模态表单的数据。这是标准 form/database 处理,完成后重定向回启动操作的原始 url。
@expose('/update/', methods=['POST'])
def update_view(self):
if request.method == 'POST':
url = get_redirect_target() or self.get_url('.index_view')
change_form = ChangeForm(request.form)
if change_form.validate():
decoded_ids = base64.b64decode(change_form.ids.data)
ids = decoded_ids.split(',')
cost = change_form.cost.data
_update_mappings = [{'id': rowid, 'cost': cost} for rowid in ids]
db.session.bulk_update_mappings(Project, _update_mappings)
db.session.commit()
return redirect(url)
else:
# Form didn't validate
# todo need to display the error message in the pop-up
print change_form.errors
return redirect(url, code=307)