Custom Active Admin form inputs for has_and_belongs_to_many relationship

activeadmin, associations, forms, ruby-on-rails, ruby-on-rails-3

Solution

After some research, I am ready to answer my own question.

First, I have to say thanks to @Lichtamberg for suggesting the fix. However, that only complicates things (also regarding security, though not an issue in this case), and doesn't help me reach my ideal solution.

Digging more, I found out that this is a very common scenario in Rails, and it's actually explained in Ryan Bates' screencast no #17.

Therefore, in Rails, if you have a has_and_belongs_to_many (short form HABTM) association, you can easily set the ids of the other associated object through this method:

profile.lifestyle_ids = [1,2]

And this obviously works for forms if you've set the attr_accessible for lifestyle_ids:

class Profile < ActiveRecord::Base
  attr_accessible :lifestyle_ids
end

In ActiveAdmin, because it uses Formtastic, you can use this method to output the correct fields (in this case checkboxes):

f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all

Also, I have simplified my form view so it's now merely this:

  form do |f|
    f.inputs # Include the default inputs
    f.inputs "Lifestlyes" do # Make a panel that holds inputs for lifestyles
      f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all # Use formtastic to output my collection of checkboxes
    end
    f.actions # Include the default actions
  end

Ok, now this rendered perfectly in the view, but if I try and submit my changes, it gives me this database error:

PG::Error: ERROR:  null value in column "created_at" violates not-null constraint
: INSERT INTO "lifestyles_profiles" ("profile_id", "lifestyle_id") VALUES (2, 1) RETURNING "id"

I found out that this is due to the fact that Rails 3.2 doesn't automatically update the timestamps for a HABTM association table (because they are extra attributes, and Rails only handles the `_id` attributes.

There are 2 solutions to fix this:

- Either convert the association into a hm:t (`has_many, :through =>`)

- Or remove the timestamps from the table

I'm going to go for 2) because I will never need the timestamps or any extra attributes.

I hope this helps other people having the same problems.

Edit: @cdesrosiers was closest to the solution but I already wrote this answer before I read his. Anyway, this is great nevertheless. I'm learning a lot.

Problem

I have a very simple model ``` class Lifestyle < ActiveRecord::Base attr_accessible :name has_and_belongs_to_many :profiles end ``` that has a `has_and_belongs_to_many` relationship with `Profile` ``` class Profile < ActiveRecord::Base attr_accessible ... belongs_to :occupation has_and_belongs_to_many :lifestyles accepts_nested_attributes_for :lifestyles end ``` I want to use ActiveAdmin to edit the Profile object, but also assign Lifestyles to a profile. It should be similar to dealing with `belongs_to :occupation`, as this is sorted out automatically by ActiveAdmin to a dropbox with the options pre-filled with available occupations. I've tried to use the `has_many` form builder method, but that only got me to show a form to type in the name of the Lifestyle and on submission, it returned an error. ``` f.object.lifestyles.build f.has_many :lifestyles do |l| l.input :name end ``` Error I get: ``` Can't mass-assign protected attributes: lifestyles_attributes ``` The perfect way for me would be to build several checkboxes, one for each Lifestyle in the DB. Selected means that the lifestyle is connected to the profile, and unselected means to delete the relation. I'm having great doubts that this is possible using ActiveAdmin and without having to create very complex logic to deal with this. I would really appreciate it if you'd give your opinion and advise me if I should go this way or approach it differently.

Original source