55 votes

Rails 3: Sélection multiple avec has_many via des associations

Je veux avoir la possibilité de sélectionner plusieurs catégories pour une publication avec plusieurs sélections.

J'ai les modèles suivants: Post, Category et 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
 

Dans mon contrôleur, j'ai quelque chose comme @post = Post.new. J'ai créé des catégories.

Et en vue j'ai:

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

Et ... où sont mes catégories? Je n'ai que "plusieurs" dans les options de sélection. Je pense que quelque chose ne va pas avec ma forme.

130voto

winfred Points 1828

Désolé pour ressusciter les morts, mais j'ai trouvé une solution beaucoup plus simple qui permet d'utiliser le contrôleur par défaut code d'action et d'utiliser le ActiveModel setter logique pour un has_many. Oui, c'est totalement magique.

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

Plus précisément, à l'aide de l' :category_ids (ou :your_collection_ids) param nom sera automatiquement dire Rails pour appeler @poste.category_ids = params[:post][:category_ids] pour définir les catégories pour que le post en conséquence, le tout sans modifier la valeur par défaut du contrôleur/échafaudage #créer et #code de mise à jour.

Oh, et il fonctionne avec has_many :quelque chose de, grâce à: :something_else, de gérer automatiquement la jointure modèle. Freaking awesome.

Donc, à partir de l'OP, il suffit de changer le champ/param nom :category_ids au lieu de :catégories.

Ce sera aussi automatiquement le modèle sélectionné les catégories de remplir le champ select comme l'a souligné lors de sur un formulaire d'édition.

Références:

À partir de la has_many API docs où j'ai trouvé cette.

Aussi l'avertissement de la forme aides guide explique cette "incompatibilité de type" lorsque vous n'utilisez pas le bon champ de formulaire/nom du paramètre.

En utilisant le bon champ de formulaire/param name, vous pouvez sécher de nouveau et de modifier les formes et de garder les contrôleurs mince, encouragés par les Rails de chemin.

remarque pour les rails 4 et forte paramètres:

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

11voto

ANATHEM Points 477

Solution finale pour organiser les catégories dans vos messages, j'espère que cela vous sera utile.

Pour utiliser plusieurs, nous avons besoin de select_tag:

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

Ou f.select ( merci beaucoup à Tigraine et Brent! ), C'est plus élégant:

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

Pour créer une action de notre contrôleur, nous avons besoin de:

 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
 

1voto

Tigraine Points 10047

Ce dont vous avez besoin est une liste d’options pour la sélection:

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

1voto

Brent Sowers Points 138

Tigraine l'a presque eu, mais vous devez spécifier un hachage vide supplémentaire:

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

0voto

naren Points 574

Comme @post n'a pas d'identifiant, il est possible que le de n'affiche pas les catégories car il n'y a pas d'association. Vous devez réussir à construire @post quelque chose comme

  @post = Post.new(:categories => Category.all)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X