Python flask admin表单:根据字段1的值约束字段2的值
我一直努力在flask admin中实现的一个功能是,当用户编辑表单时,在设置字段1后约束字段2的值 让我用文字给出一个简化的例子,实际用例更复杂。然后我将展示实现该示例的完整要点,减去约束特性 假设我们有一个数据库,它跟踪一些软件配方,以各种格式输出报告。我们示例数据库的配方表有两个配方:严重报告、ASCII Art 为了实现每个配方,我们从几种方法中选择一种。我们数据库的方法表有两种方法:制表法和打印法 每个方法都有参数。methodarg表有两个参数名用于制表结果行、显示总计和两个参数用于打印修饰字符、行到跳转 现在,对于每个食谱严肃报告,ASCII艺术,我们需要提供各自方法的参数值,将结果制成表格,打印出来 对于每个记录,recipearg表允许我们选择字段1的配方,例如严重报告和字段2的参数名称。问题是所有可能的参数名称都会显示出来,而它们需要根据字段1的值进行约束 我们可以实现什么样的过滤/约束机制,这样一旦我们选择了严重的报告,我们就知道我们将使用tablate\u results方法,这样只有rows和display\u total参数可用 我正在考虑一些AJAX向导,它检查字段1并设置字段2值的查询,但不知道如何继续 您可以通过使用要点看到这一点:单击Recipe Arg选项卡。在第一行报告中,如果您试图通过单击Methodarg值来编辑该值,则所有四个参数名称都可用,而不是只有两个Python flask admin表单:根据字段1的值约束字段2的值,python,flask,sqlalchemy,flask-sqlalchemy,flask-admin,Python,Flask,Sqlalchemy,Flask Sqlalchemy,Flask Admin,我一直努力在flask admin中实现的一个功能是,当用户编辑表单时,在设置字段1后约束字段2的值 让我用文字给出一个简化的例子,实际用例更复杂。然后我将展示实现该示例的完整要点,减去约束特性 假设我们有一个数据库,它跟踪一些软件配方,以各种格式输出报告。我们示例数据库的配方表有两个配方:严重报告、ASCII Art 为了实现每个配方,我们从几种方法中选择一种。我们数据库的方法表有两种方法:制表法和打印法 每个方法都有参数。methodarg表有两个参数名用于制表结果行、显示总计和两个参数用于
# full gist: please run this
from flask import Flask
from flask_admin import Admin
from flask_admin.contrib import sqla
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
# Create application
app = Flask(__name__)
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///a_sample_database.sqlite'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
# Create admin app
admin = Admin(app, name="Constrain Values", template_mode='bootstrap3')
# Flask views
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
class Method(db.Model):
__tablename__ = 'method'
mid = Column(Integer, primary_key=True)
method = Column(String(20), nullable=False, unique=True)
methodarg = relationship('MethodArg', backref='method')
recipe = relationship('Recipe', backref='method')
def __str__(self):
return self.method
class MethodArg(db.Model):
__tablename__ = 'methodarg'
maid = Column(Integer, primary_key=True)
mid = Column(ForeignKey('method.mid', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
methodarg = Column(String(20), nullable=False, unique=True)
recipearg = relationship('RecipeArg', backref='methodarg')
inline_models = (Method,)
def __str__(self):
return self.methodarg
class Recipe(db.Model):
__tablename__ = 'recipe'
rid = Column(Integer, primary_key=True)
mid = Column(ForeignKey('method.mid', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
recipe = Column(String(20), nullable=False, index=True)
recipearg = relationship('RecipeArg', backref='recipe')
inline_models = (Method,)
def __str__(self):
return self.recipe
class RecipeArg(db.Model):
__tablename__ = 'recipearg'
raid = Column(Integer, primary_key=True)
rid = Column(ForeignKey('recipe.rid', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
maid = Column(ForeignKey('methodarg.maid', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
strvalue = Column(String(80), nullable=False)
inline_models = (Recipe, MethodArg)
def __str__(self):
return self.strvalue
class MethodArgAdmin(sqla.ModelView):
column_list = ('method', 'methodarg')
column_editable_list = column_list
class RecipeAdmin(sqla.ModelView):
column_list = ('recipe', 'method')
column_editable_list = column_list
class RecipeArgAdmin(sqla.ModelView):
column_list = ('recipe', 'methodarg', 'strvalue')
column_editable_list = column_list
admin.add_view(RecipeArgAdmin(RecipeArg, db.session))
# More submenu
admin.add_view(sqla.ModelView(Method, db.session, category='See Other Tables'))
admin.add_view(MethodArgAdmin(MethodArg, db.session, category='See Other Tables'))
admin.add_view(RecipeAdmin(Recipe, db.session, category='See Other Tables'))
if __name__ == '__main__':
db.drop_all()
db.create_all()
db.session.add(Method(mid=1, method='tabulate_results'))
db.session.add(Method(mid=2, method='pretty_print'))
db.session.commit()
db.session.add(MethodArg(maid=1, mid=1, methodarg='rows'))
db.session.add(MethodArg(maid=2, mid=1, methodarg='display_total'))
db.session.add(MethodArg(maid=3, mid=2, methodarg='embellishment_character'))
db.session.add(MethodArg(maid=4, mid=2, methodarg='lines_to_jump'))
db.session.add(Recipe(rid=1, mid=1, recipe='Serious Report'))
db.session.add(Recipe(rid=2, mid=2, recipe='ASCII Art'))
db.session.commit()
db.session.add(RecipeArg(raid=1, rid=1, maid=2, strvalue='true' ))
db.session.add(RecipeArg(raid=2, rid=1, maid=1, strvalue='12' ))
db.session.add(RecipeArg(raid=3, rid=2, maid=4, strvalue='3' ))
db.session.commit()
# Start app
app.run(debug=True)
我认为有两种方法可以解决这个问题: 1-当Flask Admin生成表单时,在methodArg select中的每个选项标记上添加带有每个methodArg中间的数据属性。然后让一些JS代码根据所选的配方过滤选项标记 编辑 下面是在每个选项上放置数据mid属性的尝试: 然后,更新Flask Admin的form.js,让浏览器向您发送配方信息,而不是需要自动完成的methodArg名称。或者您可以在查询中发送它们,并在AjaxLoader中进行一些arg解析,因为Flask Admin对查询不进行任何解析,我想它应该是一个字符串。这样,您将保持自动完成
data: function(term, page) {
return {
query: $('#recipe').val(),
offset: (page - 1) * 10,
limit: 10
};
},
此代码段取自Flask管理员的form.js
显然,这需要一些调整和参数化,因为这样一个黑客解决方案会阻止你在app admin的其余部分使用其他ajax填充的select+直接在form.js上更新,这样会使升级Flask admin变得非常麻烦
总的来说,我对这两种解决方案和这个展示都不满意,因为无论何时,只要你想脱离框架/工具的轨道,你都可能陷入复杂的死胡同。这可能是一个有趣的功能请求/项目,对于那些愿意向Flask Admin上游贡献真正解决方案的人来说。对于灵活的数据库接口,这项功能必须具备。一个好的答案会帮助很多人。当然,数据本身可以有不同的结构,但这不是重点。增加赏金:如果您显示工作代码,我会很好奇看到1,+1用于解决方案。非常感谢您的回答。我现在患了重感冒,但很期待尝试你的想法,我不确定我是否完全理解第一个想法。你认为按照你的第一个想法做的事情会是最有力的吗?顺便问一下,如果你不得不猜测的话,这是一种应该在某个阶段进入flask admin的功能吗?汉斯·辛德勒:我会尝试为1-。我现在最大的障碍是让Flask管理员在选项标签上添加一个数据属性。droptable@:希望为1获得一些代码会使它更清晰;至于最健壮的,两者都是黑客,1也有一些缺点:您需要事先获得所有MethodArg的列表,以便JS有足够大的图片为您进行过滤。最后,我不是Flask Admin的维护者,我猜这是一个非常困难的特性,要想在总体上做到足够正确,仍然可以被认为是一个边缘问题。我尝试了第一个解决方案,但它最终变得更加麻烦,因为你基本上必须劫持WTForms而不是Flask Admin。您可以通过使用:之类的东西来摆脱monkeypatch,这也使您不必在页面中的每个选择中都使用该数据。讨论向WTForm的SelectField中的选项添加自定义属性的问题。感谢您所做的巨大努力,这让您进入了: 这表明没有简单的方法。我希望flask管理团队在某个阶段添加这个急需的特性。目前,我将忘记flask admin,从头开始创建界面。
class MethodArgAjaxModelLoader(sqla.ajax.QueryAjaxModelLoader):
def get_list(self, term, offset=0, limit=10):
query = self.session.query(self.model).filter_by(mid=term)
return query.offset(offset).limit(limit).all()
class RecipeArgAdmin(sqla.ModelView):
column_list = ('recipe', 'methodarg', 'strvalue')
form_ajax_refs = {
'methodarg': MethodArgAjaxModelLoader('methodarg', db.session, MethodArg, fields=['methodarg'])
}
column_editable_list = column_list
data: function(term, page) {
return {
query: $('#recipe').val(),
offset: (page - 1) * 10,
limit: 10
};
},