1. 程式人生 > >在flask專案中利用ilike進行搜尋

在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表示式來篩選內容以達到搜尋的目的。