railsでマルチチェックボックスになった子テーブル登録、更新を実現したい

投稿者: Anonymous

Activeレコードではないオブジェクト(Hoge)の項目をマルチチェックボックスでUserの子テーブルHogeUserに登録、更新させたい

  • UserクラスとHogeUserクラスは一対多の親子クラスです。
  • HogeオブジェクトはActiveRecordでは無くid,nameといった項目を持っています。
  • Userクラスの保存と共にHogeでチェックが入った項目をHogeUserレコードに複数登録したいです。(チェックが入ってないのは登録されないようにしたいです。)
  • バージョンアップ前のruby1.9.3,rails3.2を使ってます。

まだ中途半端で動いてないですが以下が現在の実装です。

Userクラス

class User < ActiveRecord::Base
  has_many :hoge_users

  attr_accessible ::hoge_users_attributes


  accepts_nested_attributes_for :hoge_users

  validates_associated :hoge_users
  validate :should_have_at_least_one_hoge_user

  def should_have_at_least_one_hoge_user
    errors.add(:hoge_users, 'should have at least one.') if hoge_users.empty?
  end

HogeUserクラス

class HogeUser < ActiveRecord::Base
  belongs_to :hoge
  attr_accessible :hoge, :name, :hoge_id
  validates :hoge_id, :presence => true
end

UsersControllerクラス

class UsersController < ActionController::Base
  def new
    @user = User.new(User::DEFAULT, :as => :admin)  
    respond_to do |format|
      set_objects_for_new_view
      ~

  def create
    @user = User.new(params[:user], :as => :admin)
    hoge_ids = params[:hoge_users_attributes]
    unless hoge_ids == nil
      hoge_ids.each do |hoge_id|
        @user.hoge_users.new(:hoge_id => hoge_id)
      end
    end
    respond_to do |format|
      if @user.save
      ~


   def set_objects_for_new_view
     @hoges = GET::Zone.all
     @hoge_users = @user.hoge_users.new

_form.html.erb

<%= f.fields_for :hoge_users do |hoge_user_f| %>
    <%= render :partial => 'hoge_user_form', :locals => {:f => hoge_user_f} %>
<% end %>

_hoge_user_form.html.erb

<% @hoges.each do |hoge| %>
  <tr>
    <%= hoge.name %>
    <%= check_box_tag 'hoge_users_attributes[]', hoge.id %>
  </tr>
<% end %>
  • ググってみましたがActiveレコード以外から取得する例がありませんでした。HogeをActiveRecordに入れる必要がありますでしょうか?⇒回答で解決。コード修正済み

  • 【追記】上記のようにコード修正すると登録までできるようになりました。ただチェックボックス未選択時のバリデーションが動いてくれません(hoge.idが空)。
    何か間違っている箇所が有ったりしますでしょうか?⇒回答で解決。コード修正済み

【追記2】詳細更新画面について

  • 頂いたご回答で登録処理は動作するようになりました。続いて詳細更新画面が上手く動きません。
  • 詳細でHogeUserに登録されていたものはチェックがついてる状態にしたいです。
  • チェックボックスを外したら削除されるようにしたいです。
  • 新しくチェックを入れたものは追加されるようにしたいです。

UsersControllerクラス

詳細出す前にHogeオブジェクトを取るようにしました。

class UsersController < ActionController::Base
  def show
    @user = target_user
    @hoges = GET::Zone.all

    respond_to do |format|
      format.html { render :partial => 'show' }
      ~
  def update
    @user = target_user

    respond_to do |format|
      if @user.update_attributes(params[:user], :as => :admin)
      ~

_show_user.html.erbビュー

check_boxにhoge_idを指定して登録されていたものはチェックがついてる状態になりました。
"hoge_accounts_attributes"=>{"hoge_id"=>"チェックボックスで選択したhoge.id", "0"=>{"id"=>"登録されたTBL.id"}}}
paramsは↑のものが渡るようになりました。update前にhoge.idを取得してhoge_users.newする形になるのでしょうか?

    <th>ホゲ</th>
    <td>
    <%= f.fields_for :hoge_users do |hoge_user_f| %>
      <% @hoges.each do |hoge| %>
          <%= hoge.name %>
          <%= hoge_user_f.check_box :hoge_id, {:name =>'user[hoge_users_attributes][hoge_id]'}, hoge.id %>
      <% end %>
    <% end %>
    </td>
  </tr>

Userクラス

削除にはallow_destroyオプションつけて_destroyというパラメータを渡さなければいけないとあったのですがチェックを外すと_destroyパラメータが付く形にしたいです。

class User < ActiveRecord::Base
  accepts_nested_attributes_for :hoge_users, :allow_destroy => true

解決

マルチチェックボックスというのは、その実、同じフォームの中で同じnameを持った複数個のチェックボックスでしかないです。(ただし、 name の末尾が[]であるようにする) なので、自分でチェックボックスタグを記述し、(それは、質問文にもあるように check_box_tag で実現できるはずです) Controller で適切な保存処理を記述すれば、 Hoge を ActiveRecord にする必要はないはずです。

HTML の話

HTML 上のチェックボックスは、そのフォームの submit が行われる際に、もし自分がチェックされていればNAME=valueのクエリパラメータを送付し、チェックされていなければ何も送信しません。

<input type="checkbox" name="vehicle[]" value="Bike"> Bike <br>
<input type="checkbox" name="vehicle[]" value="Car"> Car <br>

たとえば上記のような input タグたちが form タグの中に存在したときに、submit ボタンを押した時に何が起きるかというと、

  • どっちも選択されていないとき: vehicle[]についてのクエリパラメータはなし
  • Bike のみが選択されているとき: vehicle[]=Bike がクエリパラメータに含まれる
  • Car のみが選択されているとき: vehicle[]=Car がクエリパラメータに含まれる
  • Bike も Car も選択されているとき: vehicle[]=Bike&vehicle[]=Carがクエリパラメータに含まれる

として、POST が実行されます。

rails の話

rails では、名前の末尾が[]であるクエリパラメータが複数到着した場合には、 params を構築する際にそれらをまとめ上げて、配列にしてプログラムに渡してくれます。これは、 rails に限らず PHP でもそのようにクエリパラメータを処理します。

なので、

<% @hoges.each do |hoge| %>
  <tr>
    <%= hoge.name %>
    <%= check_box_tag 'hoge_users_attributes[]', hoge.id %>
  </tr>
<% end %>

で書いた後、上の input を含んでいる form の submit を受け付けるコントローラーで、 params[:hoge_users_attributes] とかしてやると、チェックが付いていた Hoge#id の一覧(配列)が取得できるはずです。

一つ注意は、上記のような動作をするため、ひとつもチェックがついていない場合には、クエリ自体が何一つ飛んでいないので、 param[:hoge_users_attributes] が nil になります。その分岐処理を記述する必要があります。

参考

http://blog.scimpr.com/2015/04/11/rails%E3%81%A7%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%81%A7%E8%A4%87%E6%95%B0%E9%81%B8%E6%8A%9E%E3%81%99%E3%82%8B%E3%81%A8%E3%81%8D%E3%80%9Ccheck_box_tag/

http://ruby-rails.hatenadiary.com/entry/20150113/1421149061#view-helpers-check_box

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *