Python flask admin表单:根据字段1的值约束字段2的值

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表有两个参数名用于制表结果行、显示总计和两个参数用于

我一直努力在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值来编辑该值,则所有四个参数名称都可用,而不是只有两个

# 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
    };
},