has_many _through 的多选框应用

简介:
As you probably already know, Rails has 2 methods of handling Many-to-Many relationships,  has_and_belongs_to_many (HABTM) and  has_many_through (HMT). Ryan Bates does a great job explaining the difference between the 2 in  Railscast #47: Two Many-to-Many. He does seem to indicate the HABTM is effectively deprecated:
Now in the other cases you might want to go with has and belongs to many...you might, but that's kind of going quickly out of date and it has some limitations that you might experience further on in development, so if you are the least bit unsure on which way to go, it's usually better to go with has many through, because that it much more flexible.
Ok, so what if I do want to use simple checkboxes in my interface, but I don't want to use the soon-to-be out of date, inflexible method?
Basically, all you need to do is implement an  _ids method for the association, and then you will be good to go. The  _ids method is automatically generated for you when you use HABTM, but since HMT is often used with models that aren't just simple join associations, there is no automatic  _ids method generated for you.
So let's create an example app where we have a User model which has many Groups through the Membership model:
$ rails example
$ cd example
$ mysqladmin -u root create example_development
$ script/generate scaffold_resource user name:string
$ script/generate scaffold_resource group name:string
$ script/generate scaffold_resource membership user_id:integer group_id:integer
$ rake db:migrate
Ok, now we've got our DB and our models, so we just need to set up the associations in each model:
#app/models/membership.rb
class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end
#app/models/group.rb
class Group < ActiveRecord::Base
  has_many :memberships
  has_many :users, :through => :memberships
end
And in the User model, we'll add the associations as well as an implementation for the group_ids method:
#app/models/user.rb
class User < ActiveRecord::Base
  has_many :memberships
  has_many :groups, :through => :memberships
  attr_accessor :group_ids
  after_save :update_groups
  #after_save callback to handle group_ids
  def update_groups
    unless group_ids.nil?
      self.memberships.each do |m|
        m.destroy unless group_ids.include?(m.group_id.to_s)
        group_ids.delete(m.group_id.to_s)
      end 
      group_ids.each do |g|
        self.memberships.create(:group_id => g) unless g.blank?
      end
      reload
      self.group_ids = nil
    end
  end
end
So what's going on here is that first we define an attribute to hold the  group_ids. Then, we define a method that will get called after this model is saved. In that callback, first check to see if  group_ids is nil, because if it is, we're going to do nothing. Then we iterate through each membership that this record has, delete it if it's  group_id is not in our new array of  group_ids. Then we remove the  group_id from the array, so that anything we have left in the  group_ids array after we've gone through all the existing memberships are groups that we should create new memberships for, for this user.
So let's see if this works. So we log into the console and first create some Groups to work with:
$ script/console
Loading development environment.
>> ('A'..'E').each{|n| Group.create!(:name => n) }
=> "A".."E"
Now we can create a User with some groups:
>> foo = User.create!(:name => 'foo', :group_ids => ['1','2','3'])
=> #<User:0x30e2c90...      
>> puts foo.memberships.collect{|m| "#{m.id} => #{m.group.name}\n" }      
1 => A
2 => B
3 => C
As you can see, we specified 3 groups that we wanted this user to have membership in. So there are 3 membership records created. Now let's take this user out of group B and put them in group D:
>> foo.update_attributes(:group_ids => ['1','3','4'])
=> true
>> puts foo.memberships.collect{|m| "#{m.id} => #{m.group.name}\n" }
1 => A
3 => C
4 => D
Notice how A and C still have the same membership id. This is important, because you don't want to just delete all the memberships and create new ones, in case there are other attributes on the membership. Of course if there are other attributes, you probably won't be using checkboxes to edit them, but you get the idea. Let's just check a couple more things to make sure this method works. First, we want to make sure if we update the model without specifying the group_ids that it remains unchanged:
>> foo.update_attributes(:name => 'foo')
=> true
>> puts foo.memberships.collect{|m| "#{m.id} => #{m.group.name}\n" }
1 => A
3 => C
4 => D
Ok, good, and last but not least, if we set group_ids to an empty array, then we want to make sure all of the memberships are deleted:
>> foo.update_attributes(:group_ids => [])
=> true
>> puts foo.memberships.collect{|m| "#{m.id} => #{m.group.name}\n" }
=> nil
Now we just need to update the views in our scaffolding to put checkboxes on the form, which I explained how to do in a previous article called  HABTM Checkboxes.
 




本文转自 fsjoy1983 51CTO博客,原文链接:http://blog.51cto.com/fsjoy/105298,如需转载请自行联系原作者
目录
相关文章
|
6天前
|
JavaScript
原生js实现复选框(全选/全不选/反选)效果【含完整代码】
原生js实现复选框(全选/全不选/反选)效果【含完整代码】
85 1
|
6天前
单选框
单选框。
10 1
|
5月前
表单单选框,多选框美化
之前总是匆匆看过,如今终于知道该怎么搞了。。。 **利用label挂钩checkbox的特点来实现。 当html代码中,label的for属性值和checkbox的id值一样的时候,label就可以控制checkbox的选择了。
20 0
|
6月前
复选框checkbox实现自定义样式
复选框checkbox实现自定义样式
30 1
|
6月前
单选、全选、反选、获得所有选中的checkbox
单选、全选、反选、获得所有选中的checkbox
|
9月前
|
JavaScript 前端开发
一行jQuery代码搞定checkbox 全选和全不选
一行jQuery代码搞定checkbox 全选和全不选
|
10月前
|
JavaScript
原生js实现全选全不选
原生js实现全选全不选
32 0
|
JavaScript 前端开发
单选与全选
结合table表格和checkbox复选框实现单选与全选功能(本文只考虑功能实现,样式不要在意,虽然极丑,但我看不到 φ(>ω<*) )。
单选与全选