Одна функция, которую я изо всех сил пытался реализовать в flask-admin, — это когда пользователь редактирует форму, чтобы ограничить значение поля 2 после установки поля 1.
Позвольте мне привести упрощенный пример на словах (фактический вариант использования более запутан). Затем я покажу всю суть, реализующую этот пример, за исключением функции «ограничения».
Допустим, у нас есть база данных, которая отслеживает некоторые программные «рецепты» для вывода отчетов в различных форматах. В таблице recipe
нашей демонстрационной базы данных есть два рецепта: «Серьезный отчет», «Искусство ASCII».
Для реализации каждого рецепта мы выбираем один из нескольких способов. Таблица method
нашей базы данных имеет два метода: "tabulate_results", "pretty_print".
Каждый метод имеет параметры. Таблица methodarg
имеет два имени параметра для "tabulate_results" ("строки", "display_total") и два параметра для "pretty_print" ("embellishment_character", "lines_to_jump").
Теперь для каждого из рецептов («Серьезный отчет», «Искусство ASCII») нам нужно указать значение аргументов соответствующих методов («tabulate_results», «pretty_print»).
Для каждой записи таблица recipearg
позволяет нам выбрать рецепт (это Поле 1, например, «Серьезный отчет») и имя аргумента (это Поле 2). Проблема в том, что показаны все возможные имена аргументов, тогда как они должны быть ограничены на основе значения поля 1.
Какой механизм фильтрации/ограничения мы можем реализовать, чтобы, выбрав «Серьезный отчет», мы знали, что будем использовать метод «tabulate_results», чтобы были доступны только аргументы «rows» и «display_total»?
Я думаю о каком-то волшебстве AJAX, которое проверяет поле 1 и задает запрос для значений поля 2, но понятия не имею, как действовать дальше.
Вы можете увидеть это, играя с сутью: нажмите на вкладку Recipe Arg
. В первой строке («Серьезный отчет»), если вы попытаетесь отредактировать значение «Методарг», щелкнув по нему, будут доступны все четыре имени аргумента, а не только два.
# 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)