Rails 3:使用has_many通过关联进行多选

Ole*_*sko 54 ruby-on-rails ruby-on-rails-3

我希望能够为多个选择的一个帖子选择几个类别.

我有下一个型号:Post,Category和PostCategory.

class Post < ActiveRecord::Base
  has_many :post_categories
  has_many :categories, :through => :post_categories
end

class Category < ActiveRecord::Base
  has_many :post_categories
  has_many :posts, :through => :post_categories
end

class PostCategory < ActiveRecord::Base
  has_one    :post
  has_one    :category
  belongs_to :post      # foreign key - post_id
  belongs_to :category  # foreign key - category_id
end
Run Code Online (Sandbox Code Playgroud)

在我的控制器中,我有类似@post = Post.new的东西.我创建了一些类别.

我认为:

<%= form_for @post do |f| %>
    <%= f.text_field :title %>
    <%= f.select :categories, :multiple => true %>
    <%= f.submit %>
<% end %>
Run Code Online (Sandbox Code Playgroud)

而且......我的类别在哪里?我在选择选项中只有"多个".我认为我的表格有问题.

win*_*red 130

很抱歉让死者复活,但我找到了一个更简单的解决方案,让人们可以使用默认的控制器操作代码,并使用ActiveModel setter逻辑进行has_many.是的,这完全是魔术.

<%= f.select :category_ids, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>
Run Code Online (Sandbox Code Playgroud)

具体来说,使用:category_ids(或:your_collection_ids)参数名称将自动告诉Rails调用@ post.category_ids = params [:post] [:category_ids]来相应地设置该帖子的类别,所有这些都不需要修改默认控制器/脚手架#create和#update代码.

哦,它适用于has_many:某事,通过:: something_else,自动管理连接模型.吓人的.

因此,从OP中,只需将字段/参数名称更改为:category_ids而不是:categories.

这也将自动使模型的选定类别填充在编辑表单上突出显示的选择字段.

参考文献:

我发现这个的has_many API文档中.

此外,表单助手指南中的警告解释了当不使用正确的表单字段/参数名称时"类型不匹配".

通过使用正确的表单字段/参数名称,您可以干燥新的和编辑表单并保持控制器的精简,如Rails方式所鼓励的那样.

注意轨道4和强参数:

def post_params
  params.require(:post).permit(:title, :body, category_ids: [])
end
Run Code Online (Sandbox Code Playgroud)

  • 这就是`collection_select`的用途:`<%= f.collection_select:category_ids,Category.all,:id,:name,{},{:multiple => true}%>` (8认同)
  • 对于在Rails 4中使用强参数的人来说,允许数组变量user:category_ids => []而不仅仅是:category_ids它是permit params列表. (7认同)

Ole*_*sko 11

在帖子中组织类别的最终解决方案,我希望它会有用.

要使用多个,我们需要select_tag:

<%= select_tag "categories", options_from_collection_for_select(Categories.all, 'id', 'name'), :multiple => true %>
Run Code Online (Sandbox Code Playgroud)

或者f.select(非常感谢Tigraine和布伦特!),它更优雅的方式:

<%= f.select :categories, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>
Run Code Online (Sandbox Code Playgroud)

在我们控制器的创建动作中,我们需要:

def create
   @post = Post.new(params[:post])

if @post.save

  params[:categories].each do |categories|
     categories = PostCategory.new(:category_id => categories, :post_id => @post.id)
     if categories.valid?
       categories.save
     else
       @errors += categories.errors
     end
  end
  redirect_to root_url, :notice => "Bingo!"
else
  render "new"
end
end
Run Code Online (Sandbox Code Playgroud)