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.
1 Answer 1
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.
-
\$\begingroup\$ 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. \$\endgroup\$ivann– ivann2014年11月27日 14:24:24 +00:00Commented Nov 27, 2014 at 14:24
Explore related questions
See similar questions with these tags.