flask admin自定义QueryAjaxModelLoader

Kar*_*bet 9 python sqlalchemy flask flask-admin

据我所知,Flask Admin支持AJAX用于外键模型加载.该瓶管理员-模型文档覆盖标题下的基本知识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"),
    )
Run Code Online (Sandbox Code Playgroud)

在名为模型的Flask管理模型视图中,该模型CuratedList具有模型的外键约束Product,我form_ajax_refs在表单创建视图中使用,以允许选择动态加载的Product项目.

form_ajax_refs = {"products": {"fields": (Product.title,)}}
Run Code Online (Sandbox Code Playgroud)

这很好地向我展示了Product模型的所有行.

但是,我目前的要求是仅使用AJAX模型加载器来显示具有特定org_id的产品,例如"Google".

尝试1号

覆盖要加入和过滤get_queryModelView类的功能.这看起来像这样:organisation_products_tableorg_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")
    )
Run Code Online (Sandbox Code Playgroud)

不幸的是,这并没有解决问题,并返回相同的行为:

def get_query(self):
    return (
        self.session.query(CuratedList)
        .filter(CuratedList.org_id == self._org_id)
    )
Run Code Online (Sandbox Code Playgroud)

它不会影响行为form_ajax_refs.

尝试2号

瓶管理员-模型文档提及使用的另一种方法form_ajax_refs,使用涉及QueryAjaxModelLoader类.

在我的第二次尝试,我继承的QueryAjaxModelLoader类,并试图覆盖值的是model,sessionfields变量.像这样的东西:

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
Run Code Online (Sandbox Code Playgroud)

然后,而不是以前的form_ajax_refs方法,我AjaxModelLoader像这样使用我的新:

form_ajax_refs = {
    "products": ProductAjaxModelLoader(
        "products", db.session, Product, fields=['title']
    )
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,无论是覆盖我的查询的值session还是model我的查询的值都不会返回AJAX加载器中的产品,并且覆盖fields仍会返回所有产品; 不只是org_id"Google"的产品.

我希望不要去的地方

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

欢迎任何建议.谢谢.

Kar*_*bet 6

感谢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()
    )
Run Code Online (Sandbox Code Playgroud)

  • 任何想法如何基于使用另一个字段而不是静态字符串(上面的“Google”)来执行此操作,即我在一个字段中选择组织,然后基于产品的下拉列表能够根据 ID 进行过滤。 (2认同)
  • 我认为我设法达到了这个目标.从QueryAjaxModelLoader我修改__init__接受和额外的过滤器arg,我也将get_list覆盖到and_过滤器.要生成表单,我必须修改AjaxSelect2Widget __call__以确保我的过滤器字段已添加到生成的html中.最后我修改了form-1.0.0.js processAjaxWidget,将过滤器信息作为查询的一部分传回, (2认同)