ダウンロード機能
今回はRubyXLを使ってStudentモデルのデータをExcel形式でダウンロードする仕組みを作成していきます。
※今回はすでに作成してあるモデルを扱ってます
※自分が分かる様にメモしているだけなので、参考にはならないかも
RubyXLはgemでインストールしてます
モデル
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
コントローラー
# 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
view
<%= link_to "生徒一覧ダウンロード", students_path(format: :xlsx), class: "btn btn-info" %>
これでRubyXLを使ったDBの値をExcelとしてダウンロードする機能は完成です。
解説
コードについて解説。
というか、このコードはコピペで取ってきたのだが、理解するまでに時間がかかったので個人的な学習の復習メモ。
モデルの定義と引数について
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の機能でパラメータを取得しているので、こんな感じになってますが、一般的には
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軸に移動して値を入れていくという仕組みが作れる。