我正在使用Flask-Admin,我对此非常满意。
docs显示了几个可用于搜索预定义字段的选项。我想允许我的用户使用引号进行搜索,例如:
"some phrase of which the order should be intact"
如何使用Flask-Admin进行此操作?
答案 0 :(得分:2)
在Flask-Admin 1.3.0中,您可以覆盖管理视图类中的_apply_search方法。请参阅下面非常简单的代码示例 - 主要来自Flask-Admin sqla示例app.py。
基本上,您希望为column_searchable_list中的列生成类似于%Your Phrase Here%的SQL。 _apply_search的标准行为是将搜索输入文本拆分为空格,并为结果数组中的每个项生成类似SQL的片段。
相反,您可以使用正则表达式查看是否已输入带引号的字符串,在引号中提取短语,创建适当的SQL类似片段,然后将其传递给生成查询的代码。
您也可以执行类似的操作来实现短语过滤 - 请参阅下面的代码中的类FilterPhrase以及它在column_filters列表定义中的使用方式。
对于更复杂的短语搜索,您可以使用Whoosh之类的内容,Postgres text searching的内置短语搜索功能(可能与SQLAlchemy-Searchable结合使用)甚至Elasticsearch
# coding: utf-8
__author__ = 'Paul'
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy import or_
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqla import ModelView
from flask.ext.admin.babel import lazy_gettext
from flask.ext.admin.contrib.sqla.filters import BaseSQLAFilter
import re
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255))
content = db.Column(db.Text, nullable=False)
def __unicode__(self):
return self.title
class FilterPhrase(BaseSQLAFilter):
def apply(self, query, value, alias=None):
stmt = "%{phrase}%".format(phrase=value)
return query.filter(self.get_column(alias).ilike(stmt))
def operation(self):
return lazy_gettext('phrase')
class PostAdmin(ModelView):
column_searchable_list = ['title', 'content']
column_filters = (
FilterPhrase(Post.title, "Title"),
FilterPhrase(Post.content, "Content"),
)
def _apply_search(self, query, count_query, joins, count_joins, search):
phrases = re.findall(r'"([^"]*)"', search)
if len(phrases) == 0:
return super(PostAdmin, self)._apply_search(query, count_query, joins, count_joins, search)
stmt = "%{phrase}%".format(phrase=phrases[0])
# The code below is taken directly from the base _apply_search
filter_stmt = []
count_filter_stmt = []
for field, path in self._search_fields:
query, joins, alias = self._apply_path_joins(query, joins, path, inner_join=False)
count_alias = None
if count_query is not None:
count_query, count_joins, count_alias = self._apply_path_joins(count_query,
count_joins,
path,
inner_join=False)
column = field if alias is None else getattr(alias, field.key)
filter_stmt.append(column.ilike(stmt))
if count_filter_stmt is not None:
column = field if count_alias is None else getattr(count_alias, field.key)
count_filter_stmt.append(column.ilike(stmt))
query = query.filter(or_(*filter_stmt))
if count_query is not None:
count_query = count_query.filter(or_(*count_filter_stmt))
return query, count_query, joins, count_joins
# Create admin
admin = Admin(app, name='Phrase Searching')
admin.add_view(PostAdmin(model=Post, session=db.session, category='Blog', name='Posts'))
def build_db():
sample_posts = [
{
'title': "de Finibus Bonorum et Malorum - Part I",
'content': "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut"
},
{
'title': "de Finibus Bonorum et Malorum - Part II",
'content': "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque"
},
{
'title': "de Finibus Bonorum et Malorum - Part III",
'content': "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium"
}
]
db.drop_all()
db.create_all()
for row in sample_posts:
post = Post(**row)
db.session.add(post)
db.session.commit()
if __name__ == '__main__':
build_db()
app.run()