困っていたこと
先日、foreign_keyを設定していたカラムのカラム名を変更したときに、エラーにはまってしまったので、備忘も兼ね記事に残しておこうと思います。
なお、実行環境は下記のとおりです。
- Rails 5.2.4.2
- PostgreSQL 12.2
前提条件
従業員の活動を記録するデータベースにおいて、従業員(employees) は 活動(activities)と多対多の関係にあります。下記のようなUIで一気に活動に参加した従業員を記録したく、
以下のコードを書きました。
作成したコード
Model
activitiesとemployeesで多対多の関係を定義し、中間テーブルをactivity_employees
としています。
class Employee < ApplicationRecord
has_many :activity_employees, dependent: :destroy
has_many :activities, through: :activity_employees
end
class Activity < ApplicationRecord
has_many :activity_employees, dependent: :destroy
has_many :employees, through: :activity_employees
end
class ActivityEmployee < ApplicationRecord
belongs_to :activity
belongs_to :employee
end
Controller
一気にデータを受け取れるよう、employee_ids: []
をstrong paramaterに入れています。
class ActivitiesController < ApplicationController
# 略
def create
@activity = @facility.activities.build(activity_params)
if @activity.save
# 処理
else
# 処理
end
end
private
def activity_params
params.require(:activity).permit(
:activity_type,
# 略,
employee_ids: [],
# 略
)
end
end
View
= form_with model: [@facility, @activity], class: "p-input-form", local: true do |f|
= f.collection_check_boxes :employee_ids, @employees, :id, :name, include_hidden: false do |t|
= t.label(class: "c-check-box__label mr-4") { t.check_box(class: "c-check-box") + t.text }
起こっていた問題
上記のコードでデータを保存しようとすると、下記のエラーが出ていました。
え。。。。staffs
テーブルにデータを保存しようとなんでしていない。。。。なぜ、staffs
テーブルの参照整合性を崩すというエラーになるの???
調べたこと
-
activity_params
として送っているデータの中身を調べました。
→ こちらに間違いはなかったので、略 -
db:schema:dump
をしてschema
ファイルを最新化したのち、値を調べました。
create_table "activity_employees", force: :cascade do |t|
t.bigint "activity_id"
t.bigint "employee_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["activity_id"], name: "index_activity_employees_on_activity_id"
t.index ["employee_id"], name: "index_activity_employees_on_employee_id"
end
…カラム名は正しい。。。おかしくなさそう。。。。
いったい何が。。。?と思っていたら、annotate
というgemによってモデルに自動生成されている記述の中に、見つけました!
# Foreign Keys
#
# fk_rails_... (activity_id => staffs.id)
# fk_rails_... (employee_id => employees.id)
カラム名がactivity_id
なのに、参照先がstaffs
テーブルだ!!
問題の原因
原因を探るためmigrationファイルをたどっていくと。。。。
▼こちらのマイグレーションの後に、
class CreateActivityEmployees < ActiveRecord::Migration[5.2]
def change
create_table :activity_employees do |t|
t.references :staff, foreign_key: true
t.references :facility_employee, foreign_key: true
t.timestamps
end
end
カラム名間違えたわ!!!…と
▼こんなマイグレーションを作っていました
class RenameStaffIdColumnToActivityEmployees < ActiveRecord::Migration[5.2]
def change
rename_column :activity_employees, :staff_id, :activity_id
end
end
その結果、カラム名は変更できたのですが、foreign_key
は変更できていなかったようです。
解決策
上記を踏まえて、以下のようなマイグレーションを作成し、実行することで解決しました。
class ChangeReferencesOfActivityEmployeesTable < ActiveRecord::Migration[5.2]
def change
remove_foreign_key :activity_employees, :staffs
add_foreign_key :activity_employees, :activities
end
end
postgresにまだ慣れていなかったので、gemのannotate
を入れてDBの中身が見えるようにしておかないと、気づけないエラーでした。*_id
というカラムでは、カラム名を変更してもforeign_key
を変更したことにはならないのね^^;
マイグレーションファイルを作るときのカラム名には、今後はより一層の注意を払おうと思います