Bottle admin custom QueryAjaxModelLoader

From what I understand, Flask Admin supports using AJAX to load a foreign key model. Flask Admin - Model Documentation describes the basics under the heading form_ajax_refs . I managed to use this successfully in many cases, but I am having problems with the level of customization that I hope to achieve. Let me clarify.

I have a Product model, an Organisation model, and a connection table for matching them, defined like this:

 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"), ) 

In the model view of the flags panel of the model named CuratedList , which has a foreign key constraint for the Product model, I use form_ajax_refs in the create view form to allow the selection of dynamically loaded Product elements.

 form_ajax_refs = {"products": {"fields": (Product.title,)}} 

This works well to show me ALL rows of the Product model.

However, my current requirement is to use the AJAX model loader to display products with a specific org_id, such as Google.

Attempt No. 1

Cancel the get_query function of the ModelView class to join the organisation_products_table and filter by org_id . It looks something like this:

 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") ) 

Unfortunately, this does not solve the problem and returns the same behavior as:

 def get_query(self): return ( self.session.query(CuratedList) .filter(CuratedList.org_id == self._org_id) ) 

This does not affect the behavior of form_ajax_refs .

Attempt number 2

Flask Admin - Model Documentation mentions another way to use form_ajax_refs , which involves using the QueryAjaxModelLoader class.

In my second attempt, I subclass the QueryAjaxModelLoader class and try to override the values ​​of the model , session or fields variables. Something like that:

 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 

And then, instead of the previous form_ajax_refs approach form_ajax_refs I use the new AjaxModelLoader like this:

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

Unfortunately, overriding session or model values ​​with my request does not return any products from the AJAX loader, and overriding fields will still return all products; not just org_id google products.

What I hope not to resort to

I would like to be able to achieve this without , in order to create a new model for each organization, as this will turn out to be scalable and poor design.

Any suggestions are welcome. Thanks.

+7
python flask flask-admin sqlalchemy
source share
1 answer

Thanks Joes for commenting on my original question, I formulated a working solution:

Override the AjaxModelLoader function as follows:

 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() ) 
+4
source share

All Articles