ビジネスパーソン・ガジェット置場 empty lot for business

営業や仕事、それに伴う生活を便利に楽にするツール、ガジェットを作ります。既にあるツールも自分用にカスタマイズ。

python: Flaskアプリの作成20 アプリ内横断検索

備忘録です。

今回はnavbarに設置した検索ボックスを使ってアプリケーション内を横断検索するコードの実装についての備忘録。

Bootstrapのnavbarに設置されている検索窓を使った横断検索

前提としてアプリケーション内に下記の4つのページとそれに対応したテーブルがあるとして実装します。

 

  • Projectsページ:プロジェクトを一覧で表示
  • Knowledgeページ:メモ的な備忘録用を一覧で表示
  • Aspirationsページ:やりたいことを一覧で表示
  • Schedulerページ:スケジュールを一覧で表示

 

その1 navbarの検索窓のコードを修正

bootstrapから検索窓付きのnavbarを使用している場合、フォームに関するコードがあると思います。そちらを下記のように修正します。

 

# navbar.html

<form  method="POST" action="{{ url_for('search')}}" class="d-flex">
    {{ form.hidden_tag() }}
  <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" name="searched">
  <button class="btn btn-outline-secondary" type="submit">Search</button>
</form>

formタグ内にPOSTのmethodと遷移先のurlを追記

inputタグ内に識別用のnameをsearchedという形で追記

 

その2 app.pyでformを作成

# app.py

# サーチフォーム
class SearchForm(FlaskForm):
    searched = StringField('Searched', validators=[DataRequired()])
    submit = SubmitField('Submit')

検索用のフォームクラスを作成します。

 

その3 navbarからフォームを渡すため下記のコードを追記

# app.py

@app.context_processor
def base():
    form = SearchForm()
    return dict(form=form)

 

その4 検索窓にキーフレーズを入れた時の動きをまとめる

# app.py

@app.route('/search', methods=['POST'])
def search():
    form = SearchForm()
    if form.validate_on_submit():
        # フォームから値を取得(検索窓に入れた文字を取得)
        keyphrase = form.searched.data
        # DBを横断検索(各モデルごとにキーフレーズを曖昧検索し、時系列で並べ替える)
        projects = Project.query.filter(Project.p_content.like('%' + keyphrase + '%'))
        projects = projects.order_by(Project.p_crated_at).all()

        knowledges = Knowledge.query.filter(Knowledge.k_content.like('%' + keyphrase + '%'))
        knowledges = knowledges.order_by(Knowledge.k_created_at).all()

        aspirations = Aspiration.query.filter(Aspiration.content.like('%' + keyphrase + '%'))
        aspirations = aspirations.order_by(Aspiration.created_at).all()

        schedulers = Scheduler.query.filter(Scheduler.s_title.like('%' + keyphrase + '%'))
        schedulers = schedulers.order_by(Scheduler.s_created_at).all()
  
        # searchというページに飛ぶ際に上記で取得したそれぞれのリストを渡す
        return render_template("search.html", form=form, searched=keyphrase, projects=projects, knowledges=knowledges, aspirations=aspirations, schedulers=schedulers)

 

その5 検索をまとめて表示する

最終的にこんな感じで横断検索し表示する。(下記表示ページのコードあり)

 

app.pyから渡されてきたリストはjinjaで適時表示するように実装する。

# search.html

{% extends "base.html" %}
{% block content %}

<br/>
<h2>You Searched For this Key Phrase...{{ searched }}</h2>
<br/>

<h3 class="mt-4 mb-3 border-bottom border-secondary border-3">Projects</h3>
{% if projects != [] %}
  {% for project in projects %}
    <div class="{% if project.p_status == 0 %}shadow p-3 mb-5 bg-body rounded{% else %}shadow p-3 mb-5 bg-secondary rounded text-white{% endif %}">
      <h2><a href="{{ url_for('project', id=project.id)}}" style="text-decoration: none; color: darkblue;">{{ project.p_title }}</a></h2>
      <small>作成日:{{ project.p_crated_at.strftime('%Y-%m-%d %H:%M') }}</small><br/>
      {% if project.p_completed_at %}
        <small>完了日:{{ project.p_completed_at.strftime('%Y-%m-%d')}}</small><br/>
      {% endif %}
      {{ project.p_content|safe }} <br/><br/>
      <a href="{{ url_for('edit_project', id=project.id )}}" class="btn btn-outline-secondary btn-sm">編集</a>
      <a href="{{ url_for('delete_project', id=project.id)}}" class="btn btn-outline-danger btn-sm">削除</a>
      <!-- ボタンをクリックした際にprojectのidを同時に渡し、add_element/p_idというurlを作る -->
      <a href="{{ url_for('add_element', p_id=project.id)}}" class='btn btn-outline-success btn-sm' >要件追加</a>
    </div>
  {% endfor %}
{% else %}
  <p>{{ searched }}に一致する項目はありませんでした...</p><br/>
{% endif %}
<br/>

<h3 class="mt-4 mb-3 border-bottom border-secondary border-3">Knowledges</h3>
{% if knowledges != [] %}
  {% for knowledge in knowledges %}
    <div class="shadow p-3 mb-5 bg-body rounded">
      <h2>{{ knowledge.k_title }}</h2>
      <small>作成日:{{ knowledge.k_created_at.strftime('%Y-%m-%d %H:%M') }}</small><br/>
      {{ knowledge.k_content|safe }} <br/><br/>
      {{ knowledge.k_tag}}  <br/><br/>
      {% if current_user.is_authenticated %}
      <a href="{{ url_for('edit_knowledge', id=knowledge.id) }}" class="btn btn-outline-secondary btn-sm">編集</a>
      <a href="{{ url_for('delete_knowledge', id=knowledge.id) }}" class="btn btn-outline-danger btn-sm">削除</a>
      {% endif %}
    </div>
  {% endfor %}
{% else %}
  <p>{{ searched }}に一致する項目はありませんでした...</p><br/>
{% endif %}
<br/>

<h3 class="mt-4 mb-3 border-bottom border-secondary border-3">Aspirations</h3>
{% if aspirations != [] %}
  {% for aspiration in aspirations %}
    <div class="{% if aspiration.status == 0 %}shadow p-3 mb-5 bg-body rounded{% else %}shadow p-3 mb-5 bg-secondary rounded text-white{% endif %}">
      <h2>{{ aspiration.title }}</h2>
      <small>作成日:{{ aspiration.created_at.strftime('%Y-%m-%d %H:%M') }}</small><br/>
      {% if aspiration.completed_at %}
        <small>完了日:{{ aspiration.completed_at.strftime('%Y-%m-%d')}}</small><br/>
      {% endif %}
      {{ aspiration.content }} <br/><br/>
      <a href="{{ url_for('edit_aspiration', id=aspiration.id )}}" class="btn btn-outline-secondary btn-sm">編集</a>
      <a href="{{ url_for('deleat_aspiration', id=aspiration.id)}}" class="btn btn-outline-danger btn-sm">削除</a>
    </div>
  {% endfor %}
{% else %}
  <p>{{ searched }}に一致する項目はありませんでした...</p><br/>
{% endif %}
<br/>

<h3 class="mt-4 mb-3 border-bottom border-secondary border-3">Schedule</h3>
{% if aspirations != [] %}
  {% for schedule in schedulers %}
   <div class="{% if schedule.s_status == 0 %}shadow p-3 mb-3 bg-body rounded{% else %}shadow p-3 mb-5 bg-secondary rounded text-white{% endif %}">
      <a href="{{ url_for('delete_schedule', id=schedule.id)}}" class="btn btn-outline-danger btn-sm float-end ms-2">削除</a>
      <a href="{{ url_for('edit_schedule', id=schedule.id)}}" class="btn btn-outline-secondary btn-sm float-end">編集</a>
      <p class="float-"><small>日程: {{ schedule.s_date.strftime('%Y-%m-%d') }}</small> <span class="fs-4">{{ schedule.s_title }}</span></p>
    </div>
  {% endfor %}
{% else %}
  <p>{{ searched }}に一致する項目はありませんでした...</p><br/>
{% endif %}
<br/><br/>

{% endblock %}