5
\$\begingroup\$

I have Gig and Singer Active Record models (standard--no customization just yet) with a many-to-many relationship through a generic join table which holds nothing but the respective ids of Gig and Singer. My form sends a given gig id and all the singers who will be attending, denoted with check boxes. I need to have the ability to check or uncheck singers. The following code works, but it does so by removing all the singers from a gig and re-adding them. This feels hacky... is there a better way? (I think this is all the code necessary but let me know if you need me to add anything)

class GigSingersController < ApplicationController

    def create
        gig = Gig.find(params[:gig_id])
        singer_ids = params[:singer_ids] # [1, 4, 5,]
        gig.singers = []
        singer_ids.each do |id|
            singer = Singer.find(id)
            gig.singers << singer
        end
        redirect_to gigs_path
    end
end

EDIT:

As requested in the comments, here are the schema and relevant models, although as I said, they are completely generic. Perhaps I didn't do a good job of making my question clear: Is the best way to create these relationships when using a checkbox to remove all existing ones and recreate them from the boxes currently checked, thereby removing any that the user unchecked on an edit?

ActiveRecord::Schema.define(version: 2019_07_19_195106) do

  create_table "gig_singers", force: :cascade do |t|
    t.integer "gig_id"
    t.integer "singer_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "gigs", force: :cascade do |t|
    t.string "name"
    t.text "notes"
    t.datetime "datetime"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "singers", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "active"
  end

class Gig < ApplicationRecord
    has_many :gig_singers
    has_many :singers, through: :gig_singers
end

class GigSinger < ApplicationRecord
    belongs_to :gig
    belongs_to :singer
end

class Singer < ApplicationRecord
    has_many :gig_singers
    has_many :gigs, through: :gig_singers

end

\$\endgroup\$
  • \$\begingroup\$ These are active-record models? \$\endgroup\$ – 200_success Jul 20 at 17:57
  • 1
    \$\begingroup\$ On database questions, it's customary to provide a database schema. This allows us to see exactly how you store your data and the inefficiencies in the code used to retrieve data from it. Please consider adding those. \$\endgroup\$ – Mast Jul 24 at 11:37
  • \$\begingroup\$ The current code is simply too small a part of what you're doing to say anything sensible about it. \$\endgroup\$ – Mast Jul 24 at 11:38
  • \$\begingroup\$ @Mast It's not a database question. I will include the things you are asking for but I can't see how they will possibly be of any help to the question I've asked. \$\endgroup\$ – thumbtackthief Jul 24 at 22:21
2
+50
\$\begingroup\$

Actually, Active Record can work with joined collection (including has_many :through) from the box.

So, you just need to pass new collection instead of old, and AR will delete excessive and add new records.

In your code, need to change create method body to:

gig = Gig.find(params[:gig_id])
singer_ids = params[:singer_ids] # [1, 4, 5,]
gig.singers = Singer.find(singer_ids)
redirect_to gigs_path
\$\endgroup\$
  • \$\begingroup\$ That's much better--thanks! \$\endgroup\$ – thumbtackthief Aug 1 at 15:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.