# Add existing models to a relation using nested attributes

I needed to add existing models to a has-many relation using nested attributes so I overwrote thumbnails_attributes=:

class Gallery < ActiveRecord::Base
has_many :thumbnails, dependent: :nullify

accepts_nested_attributes_for :thumbnails

def thumbnails_attributes=(thumbnails_attributes)
ids = thumbnails_attributes.map { |t| t['id'] }
(ids - thumbnail_ids).each do |id|
thumbnail = Thumbnail.find id
thumbnails << thumbnail
end
super(thumbnails_attributes)
end
end

class Thumbnail < ActiveRecord::Base
belongs_to :gallery
end


Given the following data (as fixture):

thumbnails.yml

one:
gallery: one

two:
gallery:


galleries.yml

one:
name: MyString


I can now run:

galleries(:one).update(thumbnails_attributes: ['id' => thumbnails(:two).id])


and galleries(:one).thumbnails will return both thumbnails.

While it works pretty well, I wanted to ask for reviews and ways to improve/clean this code.

The only issue I have is with include? (which is used by assert_includes in my tests):

galleries(:one).update(thumbnails_attributes: ['id' => thumbnails(:two).id])
galleries(:one).thumbnails.include? thumbnails(:two) # false
galleries(:one).thumbnails                           # [#<Thumbnail one>, #<Thumbnail two>]
galleries(:one).thumbnails.include? thumbnails(:two) # true


It looks like I have to reload the relation for include? to work, but I haven't found a fix nor a way to properly test it.

galleries(:one).thumbnails.to_a.include? thumbnails(:two) works, include? doesn't appear to load the association.

Couple of things to improve here:

Firstly - do not use fixtures, use factories. There is a neat library allowing you to do this with ease called FactoryGirl (for rails you will need FactoryGirlRails). Having this in place you can define factories like:

FactoryGirl.define do
factory :thumbnail do
name 'Thumbnail name if you have such a field'
other_field :other_value
end
end


And then use it as:

thumbnail = FactoryGirl.create :thumbnail


One advantage of factories is that it does not load all the data into the database for each test, but rather allows you tell it what is needed instead.

There are quite a lot of options you can add to your factories, like dynamic field values (useful when you have uniqueness validations), building parameters, building associated objects etc. All are well documented on gem github wiki.

Secondly, accepts_nested_attributes_for already provides you with a method to assign objects using their ids:

@model.children_ids << [1,2,3]


or even modify the whole association in one go

@model.children_ids = [1,2,3]


This is usually more useful in terms of web forms. If you have a form where client can select multiple objects from a list from db, all you need to do is to name those fields children_ids and rails will take care of persisting the association.

• The children_ids array is exactly what I was looking for, thank you. FactoryGirl was quite a burden to setup (I had to use database_cleaner and minitest-around) but might prove useful in the future. – ivann Nov 27 '14 at 14:24