Rails 3: множественный выбор с has_many через ассоциации

Я хочу получить возможность выбрать несколько категорий для одного сообщения с множественным выбором.

У меня есть следующие модели: 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

В моем контроллере у меня есть что-то вроде @post = Post.new . Я создал несколько категорий.

И ввиду я имею:

<%= form_for @post do |f| %>
    <%= f.text_field :title %>
    <%= f.select :categories, :multiple => true %>
    <%= f.submit %>
<% end %>

И... где мои категории? У меня есть только "несколько" в некоторых вариантах. Я думаю, что-то не так с моей формой.

5 ответов

Решение

Извините, что воскресил мертвых, но я нашел гораздо более простое решение, которое позволяет использовать код действия контроллера по умолчанию и использовать логику установки ActiveModel для has_many. Да, это просто волшебство.

<%= f.select :category_ids, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>

В частности, использование имени параметра:category_ids (или: your_collection_ids) автоматически скажет Rails, чтобы он вызывал @post.category_ids = params[:post][:category_ids] для соответствующей установки категорий для этого поста, и все это без изменения контроллера / скаффолда по умолчанию. # создать и # обновить код.

О, и это работает с has_many: что-то, через::thing_else, автоматически управляя моделью соединения. Офигенно круто.

Поэтому из OP просто измените имя поля / параметра на:category_ids вместо: category.

Это также автоматически позволит выбранным категориям модели заполнить поле выбора, как выделено в форме редактирования.

Рекомендации:

Из документации API has_many, где я нашел это.

Также в предупреждении из справочника по форме объясняется это "несоответствие типов", когда не используется правильное имя поля формы / параметра.

Используя правильное имя поля формы / параметра, вы можете высушить новые и отредактировать формы и сохранить контроллеры тонкими, как рекомендовано способом Rails.

Примечание для рельсов 4 и сильные параметры:

def post_params
  params.require(:post).permit(:title, :body, category_ids: [])
end

Окончательное решение по организации категорий в ваших постах, надеюсь, будет полезно.

Чтобы использовать несколько, нам нужно выбрать select_tag:

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

Или f.select (большое спасибо Tigraine и Brent!), Это более элегантный способ:

<%= f.select :categories, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>

В создании действия нашего контроллера нам нужно:

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

У Tigraine это почти что было, но нужно указать дополнительный пустой хеш:

<%= f.select :category_id, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>

Что вам нужно, это список опций для выбора:

<%= f.select :category_id, Category.all.collect {|x| [x.name, x.id]}, :multiple => true %>

Так как @post не имеет идентификатора, from может не отображать категории, так как нет связи. Вам нужно передать сделать сборку на @post что-то вроде

 @post = Post.new(:categories => Category.all)
Другие вопросы по тегам