web-dev-qa-db-fra.com

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.

52
Oleg Pasko

Désolé de ressusciter les morts, mais j'ai trouvé une solution beaucoup plus simple qui permet d'utiliser le code d'action par défaut du contrôleur et d'utiliser la logique de définition ActiveModel 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'utilisation du nom de paramètre: category_ids (ou: your_collection_ids) indiquera automatiquement à Rails d'appeler @ post.category_ids = params [: post] [: category_ids] pour définir les catégories de ce message en conséquence, sans modifier le contrôleur/échafaudage par défaut. #create et #update code. 

Oh, et cela fonctionne avec has_many: quelque chose, via:: quelque chose, de gérer automatiquement le modèle de jointure. Super génial.

Donc, à partir de l'OP, changez simplement le nom du champ/param en: category_ids au lieu de: categories.

Ainsi, les catégories sélectionnées du modèle seront automatiquement renseignées dans le champ de sélection tel qu'il est mis en surbrillance dans un formulaire de modification.

Références:

À partir de la documentation de l'API has_many où j'ai trouvé ceci.

Également l'avertissement du guide des aides de formulaire explique cette "incompatibilité de type" lorsque vous n'utilisez pas le nom de champ/paramètre correct.

En utilisant le nom de formulaire/paramètre approprié, vous pouvez sécher les nouveaux formulaires et les modifier, tout en maintenant les contrôleurs minces, comme le préconise la méthode Rails.

note pour Rails 4 et les paramètres forts:}

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

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
11
Oleg Pasko

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 %>
1
Tigraine

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

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

1
Brent Sowers

Comme @post n'a pas d'identifiant, il est possible que le de n'affiche pas de 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)
0
naren