【问题标题】:Flask Admin apply filters en masse as "Advanced Search"Flask 管理员将过滤器集体应用为“高级搜索”
【发布时间】:2017-03-28 21:18:27
【问题描述】:

我通过稍微扩展烧瓶管理索引视图来构建自定义“高级搜索”视图。这会将用户带到使用烧瓶管理表单渲染机器渲染的自定义表单来完成大部分工作。

表格定义如下:

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)

有没有更好的方法?

谢谢

【问题讨论】:

    标签: forms python-3.x flask query-string flask-admin


    【解决方案1】:

    好吧,事情即将变得丑陋,所以抓住你的帽子。

    这就是我最终为完成此功能而编写的代码,而且我对自己编写的代码并不感到特别自豪。它有效,但是如果您愿意,请提出一种更清洁的方法来执行此操作。

    这是高级搜索视图:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-09
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 2015-04-13
      相关资源
      最近更新 更多