2017-03-28 43 views
1

我通過稍微擴展了瓶頸管理索引視圖來設置自定義「高級搜索」視圖。這使用戶能夠使用瓶子管理表單呈現機制呈現的自定義表單來完成大部分工作。Flask Admin作爲「高級搜索」一致地應用過濾器

的形式被定義如下:

class PaperSearchForm(FlaskForm): 
    return_url = HiddenField() 
    title = StringField() 
    abstract = StringField() 
    start_date = DateTimeField() 
    end_date = DateTimeField() 
    doi = StringField() 
    pubmed_id = StringField() 
    link = StringField() 
    journals = QuerySelectMultipleField(
     query_factory=_get_model(Journal), 
    ) 
    authors = QuerySelectMultipleField(
     query_factory=_get_model(Author), 
    ) 
    keywords = QuerySelectMultipleField(
     query_factory=_get_model(Keyword), 
    ) 
    chapters = QuerySelectMultipleField(
     query_factory=_get_model(Chapter), 
    ) 
    printed = BooleanField(default=True) 
    unprinted = BooleanField(default=True) 
    submit = SubmitField('Search') 

該高級搜索模型視圖的定義如下:

from flask import flash 
from flask import redirect 
from flask import request 
from flask_admin import BaseView 
from flask_admin import expose 
from flask_wtf import FlaskForm 
from flask_login import current_user 
from .forms import PaperSearchForm 


class 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): 
     # List view generates list of models based on 'term'= from request.args.get('term', default=None) 
     # Manually setting these arguments will serve as the advanced search functionality 
     form = PaperSearchForm() # ??? 
     search = None # ??? 
     filter = None # ??? 
     flash('How to apply multiple filters?', 'error') 

     return redirect('papermodelview.index', search=search, filter=filter) # ??? 

然後,模板的定義如下:

{% extends "admin/master.html" %} 
{% import 'admin/lib.html' as lib with context %} 
{% from 'admin/lib.html' import extra with context %} {# backward compatible #} 
{% from 'admin/lib.html' import render_field with context %} 

{% block head %} 
    {{ super() }} 
    {{ lib.form_css() }} 
{% endblock %} 

{% block body %} 

    {% block navlinks %} 
    <ul class="nav nav-tabs"> 
     <li> 
      <a href="{{ return_url }}">List</a> 
     </li> 
     <li class="active"> 
      <a href="javascript:void(0)">Advanced Search</a> 
     </li> 
    </ul> 
    {% endblock %} 

    <form method="post" action="{{ url_for('advancedpapersearchview.search') }}"> 
     {{ form.return_url }} 
     {{ form.csrf_token }} 
     {{ render_field(form, form.title) }} 
     {{ render_field(form, form.abstract) }} 
     {{ render_field(form, form.start_date) }} 
     {{ render_field(form, form.end_date) }} 
     {{ render_field(form, form.doi) }} 
     {{ render_field(form, form.pubmed_id) }} 
     {{ render_field(form, form.link) }} 
     {{ render_field(form, form.journals) }} 
     {{ render_field(form, form.authors) }} 
     {{ render_field(form, form.chapters) }} 
     {{ render_field(form, form.keywords) }} 
     {{ render_field(form, form.printed) }} 
     {{ render_field(form, form.unprinted) }} 
     <div class="row"> 
      <div class="col-xs-12"> 
       {{ form.submit(class="btn") }} 
      </div> 
     </div> 
     <div class="row"> 
      <div class="col-xs-12"> 
       <a href="{{ cancel_url }}" class="btn warning"> 
        Cancel 
       </a> 
      </div> 
     </div> 
    </form> 
{% endblock %} 

{% block tail %} 
    {{ super() }} 
    {{ lib.form_js() }} 
    <script src="/static/vendor/jquery.min.js" type="text/javascript">/script> 
    {# use /static/bootstrap2/js/bootstrap.min.js if you are using bootstrap2 #} 
    <script src="/static/bootstrap3/js/bootstrap.min.js" type="text/javascript"></script> 
    <script src="/static/vendor/moment.min.js" type="text/javascript"></script> 
    <script src="/static/vendor/select2/select2.min.js" type="text/javascript"></script> 
{% endblock %} 

在紙模型視圖中,過濾器的定義如下:

class PaperModelView(MainModelView): 
    # ... 
     column_filters = [ 
     'chapter_paper_assoc.printed', 
     'journal_paper_assoc.publication_date', 
     'chapters.name', 
     'chapters.number', 
     'journals.name', 
     'authors.last_name', 
     'keywords.keyword', 
    ] 

所以,我評論了一堆???我不知道該怎麼做。如何將我的表單的字段(所選模型的特定屬性)映射到模型視圖中由'column_filters'定義的過濾器。

filters=response.args.get('filter', None) 
: -

即而不是覆蓋索引視圖搜索處理實際執行搜索,我可以代替通過傳遞這個信息的索引視圖,這與檢索此信息應用一堆過濾器

有沒有更好的方法?

謝謝

回答

0

好吧,事情會變得醜陋,所以請戴上你的帽子。

這就是我寫完成這個功能所完成的功能,而且我不是特別自豪的代碼。它的工作原理,但說,如果你喜歡它,請建議一個更乾淨的方式來做到這一點。

這裏是高級搜索視圖:

from flask import flash 
from flask import redirect 
from flask import request 
from flask import url_for 
from flask_admin import BaseView 
from flask_admin import expose 
from flask_wtf import FlaskForm 
from flask_login import current_user 
from app import admin 
from .forms import PaperSearchForm 


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