-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Has many :through association
asmolero edited this page Mar 12, 2012
·
23 revisions
Synopsys:
class Grid < ActiveRecord::Base
has_many :block_grid_associations, :dependent => :delete_all, :autosave => true, :include => :block
has_many :blocks, :through => :block_grid_associations
# for a multiselect widget: (natural choice for n-n associations)
attr_accessible :block_ids
# if you need ordered blocks inside each grid (assuming a position column in block_grid_associations table)
def block_ids=(ids)
unless (ids = ids.map(&:to_i).select{|i|i>0}) == (current_ids = block_grid_associations.map(&:block_id))
(current_ids - ids).each { |id| block_grid_associations.select{|b|b.block_id == id}.first.mark_for_destruction }
self.blocks = ids.each_with_index.map do |id, index|
if current_ids.include?(id)
(block_association = block_grid_associations.select{|b|b.block_id == id}.first).position = (index+1)
block_association
else
block_grid_associations.build({:block_id => id, :position => (index+1)})
end
end.map(&:block)
end
end
# for a nested form: (no reordering)
accepts_nested_attributes_for :blocks, :allow_destroy => true
attr_accessible :blocks_attributes
end
# for info
class Block < ActiveRecord::Base
has_many :block_grid_associations, :dependent => :delete_all
has_many :grids, :through => :block_grid_associations
end
class BlockGridAssociation < ActiveRecord::Base
belongs_to :block
belongs_to :grid
default_scope order(:position) # if you need ordered blocks
end
RailsAdmin.config do |config|
config.model Grid do
configure :block_grid_associations do
visible(false)
end
configure :blocks do
orderable(true) # only for multiselect widget currently. Will add the possibility to order blocks
# configuration here
end
end
end
Note: has_many :through
associations are not considered any differently from vanilla has_many
association. In particular, no special help is provided to edit join table attributes; you can edit indifferently the join-table or the target table.
The code above didn't work for me. There were some join models created with the position attribute set, and then the self.blocks assignment created new join models with a nil position. So, why not just do the assignment first and then update the positions? I don't know if the code is equivalent to the former, but it works for me and it's a lot simpler.
def block_ids=(ids)
unless (ids = ids.map(&:to_i).select { |i| i>0 }) == block_grid_associations.map(&:block_id)
self.blocks = Block.find(ids)
ids.each_with_index do |id, index|
block_grid_associations.select { |b| b.block_id == id }.first.position = (index+1)
end
end
end