flask 管理员自定义 QueryAjaxModelLoader
flask admin custom QueryAjaxModelLoader
据我了解,Flask Admin 支持 AJAX 用于加载外键模型。 Flask Admin - Model Documentation 涵盖标题 form_ajax_refs
下的基础知识。我已经在很多场合成功地使用了它,但是我对我希望达到的定制级别有疑问。让我详细说明。
我有一个 Product
模型,一个 Organisation
模型和一个连接 table 来关联它们,定义如下:
class Product(Base):
__tablename__ = "products"
product_uuid = Column(UUID(as_uuid=True), primary_key=True)
title = Column(String, nullable=False)
description = Column(String, nullable=False)
last_seen = Column(DateTime(timezone=True), nullable=False, index=True)
price = Column(Numeric(precision=7, scale=2), nullable=False, index=True)
class Organisation(Base):
__tablename__ = "organisations"
org_id = Column(String, primary_key=True)
org_name = Column(String, nullable=False)
products = relationship(
Product,
secondary="organisation_products",
backref="organisations"
)
organisation_products_table = Table(
"organisation_products",
Base.metadata,
Column("org_id", String, ForeignKey("organisations.org_id"), nullable=False),
Column("product_uuid", UUID(as_uuid=True), ForeignKey("products.product_uuid"), nullable=False),
UniqueConstraint("org_id", "product_uuid"),
)
在名为 CuratedList
的模型的 Flask 管理模型视图中,该模型对 Product
模型具有外键约束,我在创建视图的表单中使用 form_ajax_refs
,以允许选择动态加载的 Product
项。
form_ajax_refs = {"products": {"fields": (Product.title,)}}
这很好地显示了 Product
模型的所有行。
不过,我目前的要求是 仅使用 AJAX 模型加载器来显示具有特定 org_id 的产品,例如 "Google"。
第一次尝试
覆盖 ModelView
class 的 get_query
函数以加入 organisation_products_table
并按 org_id
过滤。这看起来像这样:
def get_query(self):
return (
self.session.query(CuratedList)
.join(
curated_list_items_table,
curated_list_items_table.c.list_uuid == CuratedList.list_uuid
)
.join(
Product,
Product.product_uuid == curated_list_items_table.c.product_uuid
)
.join(
organisation_products_table,
organisation_products_table.c.product_uuid == Product.product_uuid
)
.filter(CuratedList.org_id == "Google")
.filter(organisation_products_table.c.org_id == "Google")
)
不幸的是,这并没有解决问题,returns 与以下行为相同:
def get_query(self):
return (
self.session.query(CuratedList)
.filter(CuratedList.org_id == self._org_id)
)
它不影响 form_ajax_refs
的行为。
第 2 次尝试
Flask Admin - Model Documentation 提到了另一种使用 form_ajax_refs
的方法,它涉及使用 QueryAjaxModelLoader
class.
在我的第二次尝试中,我子class QueryAjaxModelLoader
class 并尝试覆盖它的 model
、session
或 fields
变量。像这样:
class ProductAjaxModelLoader(QueryAjaxModelLoader):
def __init__(self, name, session, model, **options):
super(ProductAjaxModelLoader, self).__init__(name, session, model, **options)
fields = (
session.query(model.title)
.join(organisation_products_table)
.filter(organisation_products_table.c.org_id == "Google")
).all()
self.fields = fields
self.model = model
self.session = session
然后我不再使用以前的 form_ajax_refs
方法,而是使用新的 AjaxModelLoader
方法,如下所示:
form_ajax_refs = {
"products": ProductAjaxModelLoader(
"products", db.session, Product, fields=['title']
)
}
不幸的是,无论是用我的查询覆盖 session
还是 model
的值 returns 都没有来自 AJAX 加载器的产品,并且仍然覆盖 fields
returns所有产品;不只是 org_id "Google".
的产品
我不希望诉诸的
我希望能够实现此 而不必 为每个组织创建一个新模型,因为这将被证明是 non-scalable 并且设计不佳.
欢迎提出任何建议。谢谢
感谢 Joes 对我最初问题的评论,我制定了一个可行的解决方案:
重写 AjaxModelLoader
函数 get_list
像这样:
def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
filters = list(
field.ilike(u'%%%s%%' % term) for field in self._cached_fields
)
filters.append(Organisation.org_id == "Google")
return (
db.session.query(Product)
.join(organisation_products_table)
.join(Organisation)
.filter(*filters)
.all()
)
经过多次试验和错误,感谢上面的帖子,我提出了一种将过滤器传递给 Ajax 模型加载器的通用方法。
这是一个通用的 class,它可以过滤外键 table。
from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader, DEFAULT_PAGE_SIZE
class FilteredAjaxModelLoader(QueryAjaxModelLoader):
additional_filters = []
def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
filters = list(
field.ilike(u'%%%s%%' % term) for field in self._cached_fields
)
for f in self.additional_filters:
filters.append(f)
# filters.append(User.list_id == 2) # Now this is passed in the constructor
# filters.append(User.is_active == 'Y')
return (
db.session.query(self.model)
.filter(*filters)
.all()
)
def __init__(self, name, session, model, **options):
super(FilteredAjaxModelLoader, self).__init__(name, session, model, **options)
self.additional_filters = options.get('filters')
用法:
一样将其传递到 form_ajax_refs
FilteredAjaxModelLoader('component', db.session, User, fields=['list_value'],
filters=[User.list_id == 2, User.is_active == 'Y'])
希望对您有所帮助。
据我了解,Flask Admin 支持 AJAX 用于加载外键模型。 Flask Admin - Model Documentation 涵盖标题 form_ajax_refs
下的基础知识。我已经在很多场合成功地使用了它,但是我对我希望达到的定制级别有疑问。让我详细说明。
我有一个 Product
模型,一个 Organisation
模型和一个连接 table 来关联它们,定义如下:
class Product(Base):
__tablename__ = "products"
product_uuid = Column(UUID(as_uuid=True), primary_key=True)
title = Column(String, nullable=False)
description = Column(String, nullable=False)
last_seen = Column(DateTime(timezone=True), nullable=False, index=True)
price = Column(Numeric(precision=7, scale=2), nullable=False, index=True)
class Organisation(Base):
__tablename__ = "organisations"
org_id = Column(String, primary_key=True)
org_name = Column(String, nullable=False)
products = relationship(
Product,
secondary="organisation_products",
backref="organisations"
)
organisation_products_table = Table(
"organisation_products",
Base.metadata,
Column("org_id", String, ForeignKey("organisations.org_id"), nullable=False),
Column("product_uuid", UUID(as_uuid=True), ForeignKey("products.product_uuid"), nullable=False),
UniqueConstraint("org_id", "product_uuid"),
)
在名为 CuratedList
的模型的 Flask 管理模型视图中,该模型对 Product
模型具有外键约束,我在创建视图的表单中使用 form_ajax_refs
,以允许选择动态加载的 Product
项。
form_ajax_refs = {"products": {"fields": (Product.title,)}}
这很好地显示了 Product
模型的所有行。
不过,我目前的要求是 仅使用 AJAX 模型加载器来显示具有特定 org_id 的产品,例如 "Google"。
第一次尝试
覆盖 ModelView
class 的 get_query
函数以加入 organisation_products_table
并按 org_id
过滤。这看起来像这样:
def get_query(self):
return (
self.session.query(CuratedList)
.join(
curated_list_items_table,
curated_list_items_table.c.list_uuid == CuratedList.list_uuid
)
.join(
Product,
Product.product_uuid == curated_list_items_table.c.product_uuid
)
.join(
organisation_products_table,
organisation_products_table.c.product_uuid == Product.product_uuid
)
.filter(CuratedList.org_id == "Google")
.filter(organisation_products_table.c.org_id == "Google")
)
不幸的是,这并没有解决问题,returns 与以下行为相同:
def get_query(self):
return (
self.session.query(CuratedList)
.filter(CuratedList.org_id == self._org_id)
)
它不影响 form_ajax_refs
的行为。
第 2 次尝试
Flask Admin - Model Documentation 提到了另一种使用 form_ajax_refs
的方法,它涉及使用 QueryAjaxModelLoader
class.
在我的第二次尝试中,我子class QueryAjaxModelLoader
class 并尝试覆盖它的 model
、session
或 fields
变量。像这样:
class ProductAjaxModelLoader(QueryAjaxModelLoader):
def __init__(self, name, session, model, **options):
super(ProductAjaxModelLoader, self).__init__(name, session, model, **options)
fields = (
session.query(model.title)
.join(organisation_products_table)
.filter(organisation_products_table.c.org_id == "Google")
).all()
self.fields = fields
self.model = model
self.session = session
然后我不再使用以前的 form_ajax_refs
方法,而是使用新的 AjaxModelLoader
方法,如下所示:
form_ajax_refs = {
"products": ProductAjaxModelLoader(
"products", db.session, Product, fields=['title']
)
}
不幸的是,无论是用我的查询覆盖 session
还是 model
的值 returns 都没有来自 AJAX 加载器的产品,并且仍然覆盖 fields
returns所有产品;不只是 org_id "Google".
我不希望诉诸的
我希望能够实现此 而不必 为每个组织创建一个新模型,因为这将被证明是 non-scalable 并且设计不佳.
欢迎提出任何建议。谢谢
感谢 Joes 对我最初问题的评论,我制定了一个可行的解决方案:
重写 AjaxModelLoader
函数 get_list
像这样:
def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
filters = list(
field.ilike(u'%%%s%%' % term) for field in self._cached_fields
)
filters.append(Organisation.org_id == "Google")
return (
db.session.query(Product)
.join(organisation_products_table)
.join(Organisation)
.filter(*filters)
.all()
)
经过多次试验和错误,感谢上面的帖子,我提出了一种将过滤器传递给 Ajax 模型加载器的通用方法。
这是一个通用的 class,它可以过滤外键 table。
from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader, DEFAULT_PAGE_SIZE
class FilteredAjaxModelLoader(QueryAjaxModelLoader):
additional_filters = []
def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
filters = list(
field.ilike(u'%%%s%%' % term) for field in self._cached_fields
)
for f in self.additional_filters:
filters.append(f)
# filters.append(User.list_id == 2) # Now this is passed in the constructor
# filters.append(User.is_active == 'Y')
return (
db.session.query(self.model)
.filter(*filters)
.all()
)
def __init__(self, name, session, model, **options):
super(FilteredAjaxModelLoader, self).__init__(name, session, model, **options)
self.additional_filters = options.get('filters')
用法:
一样将其传递到form_ajax_refs
FilteredAjaxModelLoader('component', db.session, User, fields=['list_value'],
filters=[User.list_id == 2, User.is_active == 'Y'])
希望对您有所帮助。