8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】検索結果でCSV出力する方法【Ransack】

Last updated at Posted at 2020-08-10

問題

CSV出力機能を実装した際に、そのページに検索機能(ransack gem)があることに気づいた。
検索結果をCSVファイルに反映する方法をいろいろ調べたのですが、実装するのにけっこう紆余曲折してしまったので記録として残しておきます。

前提

バージョン

Ruby 2.5
Rails 5.1
Ransack 2.1.1

実装前のソースコード

controllers/items_controller.rb
class ItemsController < ApplicationController

  def index
    @q = Item.ransack(params[:q])
    @items = @q.result
    respond_to do |format|
      format.html
      format.csv do
        send_data @items.export, filename: "items_export.csv"
      end
    end
  end

end

コントローラーにメソッドを書くとごちゃごちゃしてしまうので、実装はモデルに書きます。

models/item.rb
  def self.export
    CSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv|
      csv << %w(
        ID 名前 所有者名
      )
      all.each do |item|
        id = item.id
        name = item.name
        user = user.name

        csv << [
          id, name, user
        ]
      end
    end
  end

CSV出力の設定に関してはこの記事では触れないので、他の方の記事なども参考にしてください。

views/items/index.html.erb
<div>
  <h4>アイテム一覧</h4>

  <%= search_form_for(@q, url: items_path) do |f| %>
    <div>
      <%= link_to 'データ出力', purchase_orders_path(format: :csv),
          class: 'uk-button uk-button-secondary uk-button-small create-button' %>
    </div>

    <div>
      <div class="uk-grid-small uk-grid">
        <%= f.label :id_cont, 'ID', class: 'uk-form-label search-label' %>
        <div class="uk-form-controls">
          <%= f.search_field :id_cont, class: 'uk-input uk-form-small' %>
        </div>
        
        <%= f.label :name_cont, '名前', class: 'uk-form-label search-label' %>
        <div class="uk-form-controls">
          <%= f.search_field :name_cont, class: 'uk-input uk-form-small uk-form-width-small' %>
        </div>
        
        <%= f.label :user_cont, '所有者名', class: 'uk-form-label search-label' %>
        <div class="uk-form-controls">
          <%= f.search_field :usere_cont, class: 'uk-input uk-form-small uk-form-width-medium' %>
        </div>

        <%= f.submit '検索', class: 'uk-button uk-button-secondary uk-button-small mr15' %>
        <%= link_to 'クリア', url_for, class: 'uk-button uk-button-default uk-button-small' %>
      </div>
    </div>
  <% end %>

  <div>
    <%= will_paginate(@items, previous_label: ' &lt 前へ', next_label: '次へ &gt') %>
  </div>

  <table class="uk-table uk-table-striped table-th uk-table-small">
    <thead>
      <tr>
        <th><%= sort_link(@q, :id, 'ID') %></th>
        <th><%= sort_link(@q, :name, '名前') %></th>
        <th><%= sort_link(@q, :user, '所有者名') %></th>
      </tr>
    </thead>
    <tbody>
      <% @items.each do |item| %>
        <tr>
          <td><%= item.id %></td>
          <td><%= item.name %></td>
          <td><%= item.user%></td>
        </tr>
      <% end %>
    </tbody>
  </table>
</div>

①検索前
キャプチャ1.JPG

②検索後
キャプチャ2.JPG

上記のviewで書いたような出力ボタンの場合、検索前①も検索後②も下記のようにcontrollerのindexで取得していた値が全てCSVに出力されます。つまり、検索結果は反映されません。

出力結果

ID 名前 所有者名
1 item1 user1
2 item1 user2
3 item1 user2
4 item1 user2
5 item1 user2

解決策

出力ボタンの書き方を変えます。

views/items/index.html.erb
<div>
  <h4>アイテム一覧</h4>

  <%= search_form_for(@q, url: items_path) do |f| %>
    <div>
      <button type='submit' name='format' value='csv' class='uk-button uk-button-secondary uk-button-small create-button'>
          データ出力
      </button>

    </div>

~~~~~~~~

</div>

これで検索後②の結果を反映したCSVが出力できるようになります。

本当はRailsっぽく f.submit を使って実装したかったのですが、これを使う場合format属性はsearch_for_formに入れる必要があるんですよね。そうすると、検索機能自体が上手くいかなくなってしまうので、止む無くこの書き方にしました。
もっときれいな書き方を知ってる方がいたらぜひ教えてください。

出力結果

ID 名前 所有者名
1 item1 user1

ちなみに、実は検索後のページに遷移してなくても、検索項目に値が入っているだけでそちらを反映したデータが出力されるので注意してください。

検索前(検索項目に入力済み)③
image.png

出力結果

ID 名前 所有者名
2 item1 user2

検索ボタンをdisableにしない方法

出力ボタンを押した後、検索ボタンが押せないようになっています。
これはRailsの機能で、submitボタンが2回クリックされてしまうとHTTPリクエストが重複してしまい、バックエンド側で検出できなくなる可能性があるため、それを防止するためにある機能です。

参考:railsガイドの「3.4 入力を自動で無効にする」
https://railsguides.jp/working_with_javascript_in_rails.html#入力を自動で無効にする

image.png

データ出力した後に検索機能を使う可能性は低いと思いますが、念のため直しておきましょう。
検索ボタンにdata-disable-withを追記します。

views/items/index.html.erb
<div>

~~~~~~~~

        <%= f.submit '検索', class: 'uk-button uk-button-secondary uk-button-small mr15', 'data-disable-with' => false %>
        <%= link_to 'クリア', url_for, class: 'uk-button uk-button-default uk-button-small' %>
      </div>
    </div>
  <% end %>

~~~~~~~~

</div>

これでCSV出力後も検索ボタンが非活性にならないようになりました。

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?