Flask Admin apply filters en masse as "Advanced Search"
Alright, things are about to get ugly, so hold onto your hat.
This is what I ended up writing to accomplish this functionality, and it is code that I am not particularly proud to have written. It works, but that said, please suggest a cleaner way to do this if you feel like it.
Here is the advanced search view:
from flask import flashfrom flask import redirectfrom flask import requestfrom flask import url_forfrom flask_admin import BaseViewfrom flask_admin import exposefrom flask_wtf import FlaskFormfrom flask_login import current_userfrom app import adminfrom .forms import PaperSearchFormclass AdvancedPaperSearchView(BaseView): form_base_class = FlaskForm def __init__(self, name=None, category=None, endpoint=None, url=None, template='auth/model/paper/advanced_search.html', menu_class_name=None, menu_icon_type=None, menu_icon_value=None ): super(AdvancedPaperSearchView, self).__init__( name, category, endpoint, url or '/', 'static', menu_class_name=menu_class_name, menu_icon_type=menu_icon_type, menu_icon_value=menu_icon_value) self._template = template def is_visible(self): return False def is_accessible(self): if current_user.is_authenticated: return current_user.can_view_papers() return False @expose('/', methods=['GET', 'POST']) def index(self): form = PaperSearchForm() form.return_url.data = request.args['return_url'] self._template_args['form'] = form self._template_args['cancel_url'] = request.args['return_url'] return self.render(self._template) @expose('/search', methods=['POST']) def search(self): form = PaperSearchForm() # The goal here is to get the paper model view from the currently running app (and its admin extension). Once # the model view is here, use it to get the available filters (get their keys and operations). Use the existing # request args and add filters to them using the key and operations defined in the model view. paper_model_view = None for view in admin._views: # There must be a better way to do this, and I know this is a WTF, but I don't have the vocabulary to search # the flask admin documentation for the right way to get the instance of the model view from the admin # object. I need the *instance*, with the filters created and added to that instance by the init... so... # not clean or pretty ... and permanently restricts name of paper model view ... TODO: Fix? Rewrite? # - Chris, March 2017 if "PaperModelView object" in view.__repr__(): paper_model_view = view # ._filters contains the list of all filters # ._filter_args contains a dictionary of keys and filter objects for url construction # each filter is persisted with request.args, the query string is <flt[position]_[key]=[value]> or # <flt[position]_[key]> for filters without values # The filter is accessed by looking up the filter object with the key value, and then the filters are listed # in the order of the position provided in the query string. I am unsure whether or not they are applied in # this order, but that seems like what is happening. filters = {} i = 0 str = "flt{pos}_{key}" def __check(column, table, filter): return (column in filter.column.name and table in filter.column.table.name.__repr__()) # Sorry for this... # Iterate through each filter available for the view. Check if it's name and operation are something that # will enact a portion of the search, then add it's filter (in the format expected) to a dictionary. The index # variable i keeps track of the "count" of filters that have been added and uses this as the position of the # filter. for key, key_filter in paper_model_view._filter_args.items(): filter = key_filter[1] if hasattr(filter, 'column'): if __check("title", "papers", filter): if "FilterLike" in filter.operation.__repr__(): if form.title.data: filters[str.format(pos=i, key=key)] = form.title.data i += 1 if __check("abstract", "papers", filter): if "FilterLike" in filter.operation.__repr__(): if form.abstract.data: filters[str.format(pos=i, key=key)] = form.abstract.data i += 1 if __check("publication_date", "journal_paper", filter): if "DateSmaller" in filter.operation.__repr__(): if form.end_date.data: filters[str.format(pos=i, key=key)] = form.end_date.data.date() # Only keeps the date for the filter i += 1 elif "DateGreater" in filter.operation.__repr__(): if form.start_date.data: filters[str.format(pos=i, key=key)] = form.start_date.data.date() i += 1 if __check("doi", "papers", filter): if "FilterLike" in filter.operation.__repr__(): if form.doi.data: filters[str.format(pos=i, key=key)] = form.doi.data i += 1 if __check("pubmed_id", "papers", filter): if "FilterLike" in filter.operation.__repr__(): if form.pubmed_id.data: filters[str.format(pos=i, key=key)] = form.pubmed_id.data i += 1 if __check("link", "papers", filter): if "FilterLike" in filter.operation.__repr__(): if form.link.data: filters[str.format(pos=i, key=key)] = form.link.data i += 1 if __check("name", "journal", filter): if "FilterLike" in filter.operation.__repr__(): if form.journals.data: for journal in form.journals.data: filters[str.format(pos=i, key=key)] = journal.name i += 1 if __check("first_name", "authors", filter): if "FilterLike" in filter.operation.__repr__(): for author in form.authors.data: filters[str.format(pos=i, key=key)] = author.first_name i += 1 if __check("last_name", "authors", filter): if "FilterLike" in filter.operation.__repr__(): for author in form.authors.data: filters[str.format(pos=i, key=key)] = author.last_name i += 1 if __check("keyword", "keywords", filter): if "FilterLike" in filter.operation.__repr__(): for keyword in form.keywords.data: filters[str.format(pos=i, key=key)] = keyword.keyword i += 1 if __check("name", "chapters", filter): if "FilterLike" in filter.operation.__repr__(): for chapter in form.chapters.data: filters[str.format(pos=i, key=key)] = chapter.name i += 1 if __check("printed", "chapter_paper", filter): if "FilterEqual" in filter.operation.__repr__(): if form.printed.data == True: if form.unprinted.data == False: # Printed only filters[str.format(pos=i, key=key)] = 1 # True i += 1 else: pass # Both are True else: if form.unprinted.data == True: # Unprinted only filters[str.format(pos=i, key=key)] = 0 # False i += 1 else: pass # Both are False else: continue flash('Filters successfully applied', 'success') return redirect(url_for('paper.index_view', **filters))