#はじめに
シンプルな関係のModelで出力はみかけるのですが、少し複雑な関係のModelからCSVを出力しようと思ったときに少しはまってしまいました。
#シンプルな(親子)関係のModel
class Office
has_many :users
class User
belongs_to :office
上記のような関係のModelから取り出すときは比較的簡単にかけると思います。
Office(id: 1)に所属しているUserを取得したい場合
users = User.joins(:office).where(offices: { code: 1 })
=> #<ActiveRecord::Relation [#<User id: 1, name: "ほげ", office_id: 1>,
#<User id: 2, name: "ふが", office_id: 1>, ~~ 略 ~~]
usersをeachで回してCSVにすればいっちょ上がりですね。
けど、複雑な関係の場合はそうもいきませんでした。(自分の考え方が甘いだけかもですが)
#モデルの関係図
上記(簡素化してます)で、result_detailsに結合していってデータを引っ張ってくることとします。
##どうやったか
結論から言ってしまうと、下記の内容でControlerに直書きしました。
本当はModelに切り分けをした方がいいのですが、早く使いたいということでしたので切り分けはまた後日。
def data_create_to_csv
respond_to do |format|
format.html
format.csv do |csv|
started_on = Date.new(params[:started_on]["date(1i)"].to_i, params[:started_on]["date(2i)"].to_i, 1)
# responceは後から追加したので、左外部結合で入力されていないデータも引っ張る
details =
ResultDetail.joins(:reson, result: [diary: { user: { office: :area}}]).
eager_load(:reson, :responce, result: [diary: { user: { office: :area}}]).
where(users: { employee_code: User.active.pluck(:employee_code) }).
where(results: { result_on: started_on..started_on.end_of_month })
filename = started_on.strftime("%Y年%m月")
csv_data = CSV.generate do |csv|
columns_name = %w(##ここにはカラム名を本当は入れます。)
csv << columns_name
details.each do |detail|
values =
[
detail.result.result_on,
detail.result.user_id,
detail.result.diary.user.name,
detail.result.diary.user.role,
detail.result.diary.user.office_id,
detail.result.diary.user.office.name,
detail.result.memo,
detail.visited_time.strftime("%H:%M"),
detail.stay_time,
detail.destination_code,
detail.destination_name,
detail.destination_address,
detail.visitor,
detail.content,
detail.reson_id,
detail.reson.name,
detail.responce_id,
detail.responce&.name
]
csv << values
end
end
send_data csv_data.encode(Encoding::SJIS, invalid: :replace, undef: :replace), filename: "#{filename}.csv"
end
end
end
会社の固有名称とかあるのでそこは外させてもらいました。
send_dataとsend_fileでちょっとだけはまりました。(send_fileだといったん保存しないとerrorになる)
だいぶ駆け足でしたが、参考になれば幸いです。