LoginSignup
0
0

More than 3 years have passed since last update.

Rails こんなこともできるよ中間テーブル

Posted at

どうもチャンクノです!!
今回は中間テーブルについて書いていきます。

多分基本的に中間テーブルを作成する場合、多対多の両テーブルのidを中間テーブルに持たせると思います。
https://qiita.com/gomasio1010/items/2cff1e11d2cc82fde464
この記事のような感じ。

でも中間テーブルって必ずしもidじゃなくていいみたいです。
自分が今担当している部分の中間テーブルは

 create_table :search_condition_areas do |t|
  t.string :area_code
  t.references :search_condition, foreign_key: true
  t.timestamps
 end

こんな感じになってます。

このままだと :area_codeがただのstring型のカラムになってしまうので、

has_many :search_condition_areas, foreign_key: :area_code, primary_key: :code
has_many :search_conditions, through: :search_condition_areas

モデル側でforeign_keyとprimary_keyを指定してあげてます。
これで基本的な中間テーブルを作成した時と同じ状態になりました。

さて、気になるのはこれをcreateする時ですね。

def search_condition_params
 params.require(:search_condition).permit(
  :hoge,  :huga, :foo, area_?: [] <= ここ
 )
end

ここの?に何が入るかわかりますか?
僕は脳死で area_codes: [] にしました!!!
え、違うの?そんな声が聞こえてきそうですねえ。うんうん。違うんです。
ここは普通に area_ids: [] です。
例えば @search_condition.area_ids とコンソールで叩けばidではなく

[83] pry(main)> @search_condition.area_ids
=> ["11101000000", "13101000000", "13103000000"]

こんな感じで @search_condition に紐づく中間テーブルが持っているarea_codeが返ってきます。

[99] pry(main)> area.search_condition_ids
   (1.8ms)  SELECT `search_conditions`.`id` FROM `search_conditions` 
INNER JOIN `search_condition_areas` ON `search_conditions`.`id` = `search_condition_areas`.`search_condition_id`
WHERE `search_condition_areas`.`area_code` = '13000000000'
=> [3]

逆も然り。これは普通にsearch_conditionのidが返ってきます。
〜ids <= この部分は collection_singular_ids このメソッドを使っています。
一対多の時使えるメソッドみたいですね。
https://railsdoc.com/association
ここに色々書かれています🙆‍♂️
https://www.rubydoc.info/gems/activerecord/ActiveRecord%2FAssociations%2FClassMethods:has_many
英語できる人はこっちの方がいいっぽいです。

これがあったので、もしかしてarea_codesじゃなくてarea_idsなんじゃね?って気付くことができました!!

で、ここで気を付けなければいけないのが

@search_condition.area_ids = @new_search_condition.area_ids
@search_condition.save

コントローラーの処理でこのような記述を使用する場合ですね。
どうなると思いますか?

@search_condition.area_ids = @new_search_condition.area_ids

これ @search_condition.save の処理が走る前に上記の部分が上書き保存されてしまいます。
どうやらautosaveが自動で走ってしまうみたいです。
accepts_nested_attributes_for を使っている場合必ずautosaveがtrueになってしまうのでどうしようもないっぽいです。

User.rb

has_one :search_condition
accepts_nested_attributes_for :search_condition <= 多分ここが原因

おそらくですが親テーブルに accepts_nested_attributes_for が使用されていたらautosave: falseは使えないんでしょうね。
なので、ここでは親テーブルが保存されてから子テーブルを上書き保存する処理を書くのがいいのかなーと思います。

if @search_condition.save
  @search_condition.area_ids = new_search_condition.area_ids
else
  render "new"
end

こんな感じ。
多分もっといいやり方があるんだろうなあ。

ごちゃごちゃと色々書いてきましたが、まとめると

・中間テーブルに持たせるものは両テーブルのidじゃなくても良い。
・でもその場合はidを持たせないテーブルにforeign_keyとprimary_keyの記述を忘れずに。
・idを持たせない場合でもコントローラーのパラメーターは必ず~idsにする。
・accepts_nested_attributes_forを使用している場合はautosaveに気を付ける。

こんなところですかね😯
あんまり中間テーブルの実装には触れてこなかったのでお勉強になりました🙏
覚えることは山のようにあるなあ、、、。
では今回はこんな感じで!!
間違ってるところとかあったらコメントください!
それでは皆様良きプログラミングライフを🙏

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0