【実践】Railsにて検索結果をそのままCSV出力する(やや手抜きで)


Webアプリケーションにとって、検索画面は無くてはならないものだと思います。典型的な検索画面といえば、ヘッダー部分に検索条件を入力するフィールドがあり、その下に結果一覧が表示されるもの。

検索画面

さらに顧客の要望としてよく挙がるのが、「検索結果をExcelで見たい」というもの。みんなExcel好きですよねー(私も大好きですが)。

というわけで、CSV出力機能付きの検索画面を(なるべく手抜きで)実装してみよう思います。

筆者の環境

  • Rails 4.0.3 (別に4じゃなくても動くと思います)

Scaffold

まずはCustomerモデルを作成しましょう。サンプルアプリなので、scaffoldで済ましてしまいます。

準備ができたら、データを適当に登録しておきます。でたらめなデータでよい場合は、私はFakerというgemでテストデータを作っています。参考までに、Faker導入手順も載せておきます。

bundle install でインストールしたら、fixtureファイルを編集します。

編集し終わったら  rake db:fixtures:load するとdevelopmentデータベースにデータがロードされます。非常に簡単にテストデータが生成できました。

検索機能を実装する

検索機能も手抜きです。いや、手抜きというか、ransackという素晴らしいgemがあるので、これを利用しましょう。

いつも通り bundle install  したら、次はコントローラを編集しましょう。

ransackのGetting started通りの記述です。続いて、Viewに検索フォームを付加します。

ここまで実装できたら、実際に検索ができるか確認してみましょう。

検索機能

ちゃんと検索できてます。ransackは便利だな・・・

CSV出力機能を実装する

ではいよいよ本題です。検索結果をCSV出力できるようにしましょう。実現方法は色々あると思いますが、今回は「かなり手抜き&汎用的」な方法で実現してみたいと思います。その名も、強引にページ遷移アタックです。

検索のGETリクエストは、画面に表示する場合でもCSVに出力する場合でも、内容は変わりません。URLを見てみましょう。何らかの検索をした上でURLバーを見てください。

URL

Customerコントローラのindexアクションに?以下のパラメータが渡されるという意味ですが、CSV出力の場合も同様の検索処理なので、同じindexアクションでさばくのが良さげです。というわけで、CSV出力時は  format: :csv でリクエストが来ると仮定して、indexアクションを変更します。

formatによって処理を分けています。format.csvで呼び出される  @customers.to_csv はCustomerモデルに実装しています。(本来はプレゼン層の仕事なので、モデルに書くべきでないとは思いますが、今回は単純化のためにモデルに実装しています。)

csvをrequireする必要があるので、config/application.rbも編集します。

RailsCastsまんまのコードですが笑。Railsを再起動したらCSV出力ができるはずです。画面で何か検索して結果が表示されたら、そのURLの  /customers?utf8=... の部分を  /customers.csv?utf8=... に変更してEnterを押してみてください。無事CSVがダウンロードできたでしょうか?

Excelで開くために

ダウンロードしたCSVファイルをExcelで開いてみましょう。上手く表示されている場合と、そうでない場合があると思います。それは『日本語を使用しているかどうか』によります。日本語を使用している場合は、日本語が文字化けし、場合によっては列もずれます。

文字化け

かくも無残な姿。。。これはExcelでUTF8のCSVを扱うには、BOM付きで出力する必要があるからです。少しコントローラを修正しましょう。

send_data メソッドを  send_csv メソッドに書き換えました。なんだ、こんな便利なメソッドあったのか・・って無いですよ、もちろん。自力で ApplicationControllerに実装するんです。

これでBOM付きUTF8で出力できるはずです。再度CSVを出力し、Excelで開くと・・・

UTF8BOM

グレート。ひとまず、CSV出力については完了です。

CSV出力ボタンを実装する

さて最後のトドメが必要です。CSV出力ボタンを配置しなければなりません。さー、どうやって実装しましょうか?

まず最初に思い付きそうなのが、同じform内にもう一つボタンを配置するという案です。しかしこれだとindexアクションにformat無し(=html)でリクエストすることになるので、コントローラ内で処理を分岐するのが面倒臭そうです。そこで、今回はjavascriptを使用して、同じURLにフォーマットだけを変更して遷移するという、なんとも強引な方法で実装してみたいと思います。

まずは、ヘルパーにCSV出力ボタンのコードを実装します。色んな画面で使い回せそうなので、ApplicationHelperに定義することにしましょう。

クリックされると  reloadWithFormat というfunctionを呼び出すようにしています。このfunctionはcustomers.js.coffeeにでも定義しましょう。

何をしてるかというと、元のURLにフォーマットで指定された文字列を挿入して遷移してるだけ。遷移するといっても、遷移先が  send_data してるので、ブラウザ上の表示は変わらないです。では最後に、このボタンをViewに組み込みましょう。

どこでもいいんですが、私は結果一覧の下に配置しました。

最後に動作を確認おきましょう。適当な条件で検索してExportボタンをクリックすると・・・

Export

完成CSV

無事ダウンロードしたCSVをExcelで開けました。 完成です! お疲れ様でした!

URLを加工して遷移するなんて少々強引な気もしますが、コントローラの処理はスッキリしますし、出力ボタンも使い回しが効きます。単純なアプリなら充分実用に耐えうるのではないでしょうか。

他にもっと良い方法があれば教えて下さいね。

関連する記事