flask admin自定义QueryAjaxModelLoader

据我所知,Flask Admin支持AJAX用于外键加载。 Flask Admin - Model Documentation涵盖了标题form_ajax_refs下的基础知识。 我已经成功地在很多场合成功地使用了这个功能,但是我希望能够实现定制化水平方面的问题。 让我详细说明一下。

我有一个Product模型,一个Organisation模型和一个连接表来关联它们,定义如下:

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”。

尝试1号

覆盖get_query的功能ModelView类加入就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")
    )

不幸的是,这并不能解决问题,并返回与以下相同的行为:

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类。

在第二次尝试中,我QueryAjaxModelLoaderQueryAjaxModelLoader类,尝试覆盖它的modelsessionfields变量的值。 像这样的东西:

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']
    )
}

不幸的是,用我的查询覆盖sessionmodel的值是否session返回AJAX加载器中的任何产品,并且覆盖fields仍会返回所有产品; 不只是org_id“Google”的产品。

我希望不去的地方

我希望能够在不需要为每个组织创建新模型的情况下实现这一目标,因为这将证明是不可扩展的且设计不好。

欢迎任何建议。 谢谢。


感谢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模型加载器。

这是一个通用类,可以在外键表上进行筛选。

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一样传递给QueryAjaxModelLoader

FilteredAjaxModelLoader('component', db.session, User, fields=['list_value'],
                                filters=[User.list_id == 2, User.is_active == 'Y'])

希望有所帮助。

链接地址: http://www.djcxy.com/p/86243.html

上一篇: flask admin custom QueryAjaxModelLoader

下一篇: Columns property not aligning correctly