Search code examples
ruby-on-railscsvdatatablesexport-to-csv

Exporting 2 tables to CSV in Rails [how to select specific columns?]


I have been trying to export a database to CSV in Rails. After many trials and errors, I finally got it to work, and right now my CSV consists of the full 2 tables.

However, I can't seem to be able to extract only certain columns from each table - hourlog name, hourlog description, hourlog hours, hourlog created_at and project name (marked in red in the photo attached).

All solutions I found online were for extracting the whole table.

This is my model (for hourlog):

class Hourlog < ApplicationRecord

    belongs_to :user
    belongs_to :project
    accepts_nested_attributes_for :project
    validates :name, presence: true
    validates :description, presence: true
    validates :hours, presence: true
    validates :date, presence: true
    validates :project, presence: true

      def self.to_csv
        CSV.generate do |csv|
          csv << column_names
          all.each do |hourlog|
            csv << (hourlog.attributes.values_at(*column_names) + hourlog.project.attributes.values )
          end
        end
      end
end

This is my controller (for hourlog):

def index 
    @user = User.all
    @filter = Filter.first
    @project = Project.all
    [... a bunch of code that defines @hourlogs...] 

    respond_to do |format|
      format.html
      format.csv { render text: @hourlogs.to_csv }
    end
end 

This is the view:

<%= link_to "CSV", hourlogs_path(format: "csv") %>

This is the schema for hourlogs:

create_table "hourlogs", force: :cascade do |t|
    t.string   "name"
    t.string   "project"
    t.text     "description"
    t.integer  "hours"
    t.datetime "created_at",     null: false
    t.datetime "updated_at",     null: false
    t.integer  "user_id"
    t.datetime "date"
    t.integer  "project_id"
    t.datetime "date_from"
    t.datetime "date_to"
    t.string   "project_filter"
    t.index ["project_id"], name: "index_hourlogs_on_project_id", using: :btree
    t.index ["user_id"], name: "index_hourlogs_on_user_id", using: :btree
  end

And this is the schema of project:

create_table "projects", force: :cascade do |t|
    t.string   "name"
    t.text     "description"
    t.string   "plan"
    t.integer  "planhours"
    t.integer  "thours"
    t.datetime "created_at",                    null: false
    t.datetime "updated_at",                    null: false
    t.datetime "plandate"
    t.datetime "starthourtrack"
    t.integer  "duration"
    t.text     "notes"
    t.boolean  "status",         default: true
  end

Any help will be appreciated, I have been struggling for 10 hours now, and can't seem to get it. I tried creating a separate index.csv.erb file, outlining the header names, selecting hourlog.name, instead of hourlog.attributes.values_at(*column_names), but I can't seem to get the syntax/logic right. Thanks!

[EDIT: SOLUTION] It looks like I simply needed some brackets - I added specific column names to limit the # of columns displayed, and then I wrote out each column I needed (hourlog.name, hourlog.description etc.) - I didn't realize I needed to put those in [], after "csv <<" which did the trick.

def self.to_csv
        CSV.generate do |csv|
          column_names = %w(Project Name Description Hours Date)
          csv << column_names
          all.each do |hourlog|
            csv << [hourlog.project.name, hourlog.name, hourlog.description, hourlog.hours, hourlog.created_at]
          end
        end
      end

What my CSV export looks like right now - I have marked in red the only columns I want to have in my CSV file


Solution

  • It looks like I simply needed some brackets - I added specific column names to limit the # of columns displayed, and then I wrote out each column I needed (hourlog.name, hourlog.description etc.) - I didn't realize I needed to put those in [], after "csv <<" which did the trick.

    def self.to_csv
            CSV.generate do |csv|
              column_names = %w(Project Name Description Hours Date)
              csv << column_names
              all.each do |hourlog|
                csv << [hourlog.project.name, hourlog.name, hourlog.description, hourlog.hours, hourlog.created_at]
              end
            end
    end