3
3

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 5 years have passed since last update.

Excelダウンロード機能の実装

Posted at

ダウンロード機能

今回はRubyXLを使ってStudentモデルのデータをExcel形式でダウンロードする仕組みを作成していきます。
※今回はすでに作成してあるモデルを扱ってます
※自分が分かる様にメモしているだけなので、参考にはならないかも

RubyXLはgemでインストールしてます


```student.rb
  def self.to_xlsx(students) #引数にテーブルを受け渡す
    # binding.pry
    require "rubyXL"
     @workbook = RubyXL::Workbook.new #Excel新規作成
     @worksheet = @workbook[0] #最初のシートを操作

    # Excel上のヘッダー作成
    student_row = %w(氏名 EMAIL 生年月日) #Excelに表示するヘッダーの値を変数に代入
     student_row.each_with_index do |val, row_idx|
       @worksheet.add_cell(0, row_idx, val)
     end

    # 実際の値をDBから取得
    students.each.with_index(1) do |student, row_idx|
  %w(name sex age).each_with_index do |name, col_idx|
    @worksheet.add_cell(row_idx, col_idx, student.__send__(name))
    end
  end
   @workbook.stream.read #バイナリーで値を取得
end

```students_controller.rb
# indexアクション
  def index
    @q = Student.ransack(params[:q])
    @departments = Department.all
    @students = @q.result(distinct: true)
    excel_dl_format #indexでExcelのダウンロードを扱う為、設定した定義を読み込む
  end

  # Excel_DLフォーマットアクション
   def excel_dl_format
      respond_to do |format|
        format.html
          format.xlsx do
            send_data Student.to_xlsx(@students),#indexアクションの@studentsをsendしている
            filename: "生徒一覧.xlsx".encode(Encoding::Windows_31J)
          end
      end
    end


```students/index.html.erb

<%= link_to "生徒一覧ダウンロード", students_path(format: :xlsx), class: "btn btn-info" %>

これでRubyXLを使ったDBの値をExcelとしてダウンロードする機能は完成です。

解説

コードについて解説。
というか、このコードはコピペで取ってきたのだが、理解するまでに時間がかかったので個人的な学習の復習メモ。

モデルの定義と引数について

student.rb
def self.to_xlsx(students) ⇦この部分でアクションを定義しているのだが、引数も与えています。
end

その引数はどこから値を持ってきているのかというと

このstudentsと紐づいてます⇨ students.each.with_index(1) do |student, row_idx|
                         %w(name sex age).each_with_index do |name, col_idx|
                         @worksheet.add_cell(row_idx, col_idx, student.__send__(name))

引数の名前を
def self.to_xlsx(students) としてますが、
()内の名前は何でもよくて、(hoge)とかでもいいです。
ただしその場合は、

hoge.each.with_index(1) do |student, row_idx|
%w(name sex age).each_with_index do |name, col_idx|
@worksheet.add_cell(row_idx, col_idx, student.__send__(name))

と、する必要があります。

引数がある場合は、必ず何かを参照しているので、中身を確認し、
どこから情報を取ってきているのかを把握することが大事。

フォーマットについて

コントローラーにて下記のようなフォーマットを定義しているのだが、それの解説

  # Excel_DLフォーマットアクション
   def excel_dl_format
      respond_to do |format|
        format.html
          format.xlsx do
            send_data Student.to_xlsx(@students),#indexアクションの@studentsをsendしている
            filename: "生徒一覧.xlsx".encode(Encoding::Windows_31J)
          end
      end

send_data

 send_data Student.to_xlsx(@students), ⇦この部分に焦点をあてて解説

まず、Student.to_xlsx()という部分、ここでStudentモデルのデータを参照して、
モデルで定義したdef self.to_xlsx(students)へフォーマット情報を引き渡している役割。だと思う。
イメージそんな感じ。使って感覚的に学んだので、詳しい方教えてください。

※ self.to_xlsx()とStudent.to_xlsx()ってそもそも名前違うけど、そこんとこどうなのって思うかもですが、selfはそのクラス内のという意味なのでself.to_xlsx() == Student.to_xlsx() と考えて良いです

で、Student.to_xlsx(@students)と、引数に@studentsを与えることにより、実際の値を取得できる。

なぜそれで取得できているのかというと、indexアクションで、@studentsにStudentの値をすべて入れているからです!

    @q = Student.ransack(params[:q])
    @departments = Department.all
    @students = @q.result(distinct: true)

僕の場合、ransackの機能でパラメータを取得しているので、こんな感じになってますが、一般的には

students_controller.rb
def index
    @students = Student.all
end

とかで定義すれば値を取得できます。

イメージ

キーワードを抜粋するとこんな感じでデータのやりとりをしてるイメージ

・self.to_xlsx(students) <==> students.each.with_index(1) do 
・Student.to_xlsx() <==> self.to_xlsx()
・send_data Student.to_xlsx(@students) <==> @students = @q.result(distinct: true)

逆にわかりにくいかもだけど、こんな感じ。。。

ループ(each)について解説

student_row.each_with_index do |val, row_idx|
   @worksheet.add_cell(0, row_idx, val)
end

RubyXLではループする場合にも、x座標 と y座標を明確にする必要がある。
上記コードでいうとval = y row_idx = xになる
そしてその中のコードでは@worksheet.add_cell(0, row_idx, val)となっており、x yで示すならば
@worksheet.add_cell(y, x, "値")となる。

ということを踏まえて
@worksheet.add_cell(0, row_idx, val)になっているので

y座標が0
x座標がrow_idx
値がval

student_rowには 名前、性別、年齢が格納されているので、
最初の行にstudent_rowの値がループされる。という仕組みが出来る。

 students.each.with_index(1) do |student, row_idx|
  %w(name sex age).each_with_index do |name, col_idx|
   @worksheet.add_cell(row_idx, col_idx, student.__send__(name))
    end
  end

続いてこの部分について解説
studentsをループしています。このstudentsはself.to_xlsx(students)で引数に渡している値になります。

studentsの値がstudentに入って、row_idxでx軸に入ってる値を順に表示していきます。
そして、ループの中で更にループが発生しています。
ここで実際のDBの中身(Studentの値)を取得してます。

                        ↓ここでkey指定
  %w(name sex age).each_with_index do |name, col_idx|
   @worksheet.add_cell(row_idx, col_idx, student.__send__(name))
                                                            ↑ここでkey指定 

上記のkey(今回はkeyをnameにしてます)を設定してパラメーター取得
col_idxでx軸へ値を入れていく

@worksheet.add_cell(row_idx, col_idx, student.__send__(name))

上記コードでは、add_cellでセルの操作を取得しつつ
y軸にrow_idx, x軸にcol_idx 値にstudent.send(name)
とすることで、大外のrow_idxがループし終わった後に内側のrow_idxが発動。y軸に移動して値を入れていくという仕組みが作れる。

3
3
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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?