在flask專案中利用ilike進行搜尋
在專案中的表格上方,通常都會新增一個搜尋視窗,按輸入內容進行搜尋。搜尋過程是前端輸入內容,提交一個表單到相應的路由函式,表單內容在函式中獲取是通過request.args.get(‘q’, ”)。我這裡搜尋表單的id是q,如果獲取的內容不存在則內容為空,則不過濾,否則通過Model.query.filter()來過濾相應的內容。下面通過不同部分來看看具體實現。
實現原理是Postgresql的LIKE語句,在專案中是用過SQLAlchemy的ilke方法來實現。
jinja2模板
搜尋框是通過巨集定義的。
{% import 'macros/form.html' as f with context' %} {{ f.search('admin.users') }}
下面看看巨集的具體實現辦法。templates/macros/form.html
。重點看下面兩段程式碼:
...
{# Render a form tag that contains a CSRF token and all hidden fields. #}
{%- macro form_tag(endpoint, fid='', css_class='', method='post') -%}
<form action="{{ url_for(endpoint, **kwargs) }}" method="{{ method }}"
id="{{ fid }} " class="{{ css_class }}" role="form">
{{ form.hidden_tag() }}{{ caller () }}
</form>
{%- endmacro -%}
{# Render a form for searching. #}
{%- macro search(endpoint) -%}
{% call form_tag(endpoint, method='get') %}
<label for="q"></label>
<div class="input-group md-margin-bottom" >
<input type="text" class="form-control"
id="q" name="q" value="{{ request.args.get('q', '') }}"
placeholder="Search by typing, then press enter...">
<span class="input-group-addon">
<i class="fa fa-fw fa-search"></i>
</span>
</div>
{% endcall %}
{%- endmacro -%}
我們在模板中呼叫的f.search巨集,並傳入了admin.users這個endpoint,在search這個巨集通過call form_tag這個巨集來渲染一個表單,search巨集 call內部的內容是放在form_tag的 {{ caller () }}處的,連起來看就是渲染了一個有csrf保護的表單,並且傳入有搜尋內容的輸入視窗,提交出發的動作是路由到admin.users這個檢視函式。所以接下來看看試圖函式的處理。
views
也是關注程式碼處理搜尋的部分。
@admin.route('/users', defaults={'page': 1})
@admin.route('/users/page/<int:page>')
def users(page):
search_form = SearchForm()
bulk_form = BulkDeleteForm()
sort_by = User.sort_by(request.args.get('sort', 'created_on'),
request.args.get('direction', 'desc'))
order_values = '{0} {1}'.format(sort_by[0], sort_by[1])
paginated_users = User.query \
.filter(User.search(request.args.get('q', ''))) \
.order_by(User.role.asc(), User.payment_id, text(order_values)) \
.paginate(page, 50, True)
return render_template('admin/user/index.html',
form=search_form, bulk_form=bulk_form,
users=paginated_users)
表單提交的內容作為引數傳入了.filter(User.search(request.args.get('q', '')))
。這裡呼叫了model User的search方法,稍後再分析那邊的程式碼,這裡還可以看到,我們最後傳入給模板的users其實是有過濾有排序並且分頁的物件。
接下來才到搜尋的重點,model部分。
User
User是專案中定義使用者的模型,通過SQLAlchemy和資料庫建立關聯。上面說到了搜尋是用過User的search方法實現的,程式碼如下:
@classmethod
def search(cls, query):
"""
Search a resource by 1 or more fields.
:param query: Search query
:type query: str
:return: SQLAlchemy filter
"""
if not query:
return ''
search_query = '%{0}%'.format(query)
search_chain = (User.email.ilike(search_query),
User.username.ilike(search_query))
return or_(*search_chain)
view函式中傳入的表單內容其實就是search函式的query引數(查詢資訊)。首先我們看下Postgresql的LIKE語句,ILIKE只是讓匹配內容與大小寫無關,lower(a) LIKE lower(other)和a ILIKE other等效。具體教程。
PostgreSQL的LIKE操作符是用來反對使用萬用字元的模式匹配的文字值。如果搜尋表示式可以匹配的模式表示式,LIKE運算將返回true,也就是1。有兩個萬用字元與LIKE運算子一起使用:
百分號 (%)
下劃線 (_)
百分號表示零個,一個或多個數字或字元。下劃線代表一個單一的數字或字元。這些符號可以被組合使用。
那麼結合我們傳入的內容,search_query查詢資訊實際就是包含了我們輸入內容的字串。比如輸入的是123
,那麼可能aa123bb
, 123df23
等都是匹配的內容。然後將查詢資訊傳入ilike,這個ilike是SQLAlchemy中對列使用的方法,就是為了構建pg資料的ILIKE語句。這裡為了讓輸入內容匹配多行,我們通過構建search_chain來讓搜尋內容匹配兩行,並通過or_方法生成或
關係的連線表示式。
最後就是filter接收這個SQL表示式來篩選內容以達到搜尋的目的。