我在一个应用程序的版本一中有以下(简化的(结构,我正在将大部分逻辑移植到版本二的Flask Admin:
- 门户
- 这是所有其他资源的基础资源
- 路由
/
导致门户的列表视图 - 路线
/new
通向创建门户的表单 - 路由
/<portal_slug>/edit
导致用于更新门户的表单
- 账户
- 直接与门户建立关系,因此v1的应用程序路由如下
- 路线
/<portal_slug>/accounts
通向列表视图 - 路由
/<portal_slug>/accounts/new
导致创建帐户的表单 - 路由
/<portal_slug>/accounts/<account_id>[/edit]
导致更新帐户的表单
我对该结构的推理是用户友好的url,此外,从门户分支似乎只是合乎逻辑的,因为它是应用程序的主要资源。
使用上面的url结构,我有一个url_value_preprocessor
函数,它将实体加载到Flask的g
变量中,以便在视图中使用。
@app.url_value_preprocessor
def load_url_objects(endpoint, values):
if not values:
return
if "portal_slug" in values:
g.portal = Portal.query.filter_by(slug=values.pop("portal_slug", None)).first()
if g.portal:
# save portal_id to session for use elsewhere
session["portal_id"] = g.portal.id
if "account_id" in values:
g.account = g.portal.accounts.filter_by(id=values.pop("account_id", None)).first()
我的问题
使用Flask Admin时,如何维护此url结构?我该如何实现这种行为?
障碍1:对于连接到门户的所有视图(帐户、报告、作业等(,portal_slug
是一个过滤器,需要在之前应用以列出视图呈现。这是因为每个门户都有额外的自定义字段,这些字段需要在所有门户的静态字段之上的列表视图中呈现。
障碍2:在导航栏(而不是内容区(中提供下拉菜单,无论用户处于哪个视图中,都可以切换门户。每当单击任何导航栏链接以获取附加到门户的视图时,这也可用于选择初始门户(或默认为第一个门户(。这就是我在上面使用session["portal_id"] = g.portal.id
的原因,以便使最终用户能够轻松地在选项卡之间导航(维护上次查看的门户(。
障碍3:我想无论何时排序、搜索、过滤,让所有的url都能反映这种行为都是非常困难的。或者更可能的是,我只是无法弄清楚需要覆盖的所有函数。
结构的最小工作示例
from flask_admin.contrib.sqla import ModelView
from flask_sqlalchemy import SQLAlchemy
from flask import Flask, session, g
from flask_admin import Admin
from sqlalchemy import event
from slugify import slugify
app = Flask(__name__)
db = SQLAlchemy(app)
admin = Admin(app, url="/", template_mode="bootstrap4")
class Portal(db.Model):
__tablename__ = "portals"
id = db.Column(db.Integer, primary_key=True)
display_name = db.Column(db.String(100), nullable=False, unique=True)
slug = db.Column(db.String(20), nullable=False, unique=True)
accounts = db.relationship("Account", backref="portal", lazy="dynamic", cascade="all, delete")
@staticmethod
def slugify(target, new_value, old_value, initiator):
if new_value and (not target.slug or new_value != old_value):
new_slug = slugify(new_value, lowercase=True, max_length=20, word_boundary=True, save_order=True)
target.slug = new_slug
class Account(db.Model):
__tablename__ = "accounts"
id = db.Column(db.Integer, primary_key=True)
portal_id = db.Column(db.Integer, db.ForeignKey("portals.id"), nullable=False)
client_name = db.Column(db.String(50), nullable=False)
event.listen(Portal.display_name, "set", Portal.slugify, retval=False)
@app.shell_context_processor
def load_url_objects(endpoint, values):
if not values:
return
if "portal_slug" in values:
g.portal = Portal.query.filter_by(slug=values.pop("portal_slug", None)).first()
if g.portal:
# save portal_id to session for top navbar link routes
session["portal_id"] = g.portal.id
if "account_id" in values:
g.account = g.portal.accounts.filter_by(id=values.pop("account_id", None)).first()
class PortalView(ModelView): pass # /portals
class AccountView(ModelView): pass # currently /accounts, but need it to be /<portal_slug>/accounts for all links related to account(s)
admin.add_view(PortalView(Portal, db.session, name="Portals", endpoint="portals"))
admin.add_view(AccountView(Account, db.session, name="Accounts", endpoint="accounts"))
@app.before_first_request
def prep_db():
db.create_all()
if __name__ == "__main__":
app.config.update({
"SQLALCHEMY_TRACK_MODIFICATIONS": False,
"SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:"
})
app.run(debug=True)
我有一个类似的问题,我通过覆盖get_query和get_query_count方法来获得我的index_view过滤器并覆盖我的端点来解决它。
您的路线为/<portal_slug>accounts/<account_id>[/edit]-我建议您@express("/<account_id>/edit并重写您的edit_view函数
class PostView(ModelVieW):
...
all your main required code goes here
including your expose..
@expose("/<account_id>/edit")
def my_edit_view(self):
... ...
you need to take the account_id param and pass it
to flask.request as param id=<account_id>, this is
how flask-admin read the id.
return super().edit_view()
class PostPublishView(PostView):
def is_visible(self):
return False
def get_query(self):
return self.session.query(self.model).filter(self.model.status=='publish')
def get_count_query(self):
return self.session.query(func.count('*')).filter(self.model.status=='publish')
admin.add_view(PostPublishView(Post, db.session, name="Publish", endpoint='/post/publish'))
admin.add_view(PostView(Post, db.session, name='Post', endpoint='post'))