0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

foreign_keyを設定しているカラムのカラム名を変更したときにはまったこと。

Last updated at Posted at 2020-08-14

困っていたこと

先日、foreign_keyを設定していたカラムのカラム名を変更したときに、エラーにはまってしまったので、備忘も兼ね記事に残しておこうと思います。

なお、実行環境は下記のとおりです。

  • Rails 5.2.4.2
  • PostgreSQL 12.2

前提条件

従業員の活動を記録するデータベースにおいて、従業員(employees) は 活動(activities)と多対多の関係にあります。下記のようなUIで一気に活動に参加した従業員を記録したく、

ui.png

以下のコードを書きました。

作成したコード

Model
activitiesとemployeesで多対多の関係を定義し、中間テーブルをactivity_employeesとしています。

models/employee.rb
class Employee < ApplicationRecord
  has_many :activity_employees, dependent: :destroy
  has_many :activities, through: :activity_employees
end
models/activity.rb
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 }

起こっていた問題

上記のコードでデータを保存しようとすると、下記のエラーが出ていました。
error.png
え。。。。staffsテーブルにデータを保存しようとなんでしていない。。。。なぜ、staffsテーブルの参照整合性を崩すというエラーになるの???

調べたこと

  1. activity_paramsとして送っているデータの中身を調べました。
    → こちらに間違いはなかったので、略

  2. db:schema:dumpをしてschemaファイルを最新化したのち、値を調べました。

db/schema.rb
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によってモデルに自動生成されている記述の中に、見つけました!

models/activity_employee.rb
# Foreign Keys
#
#  fk_rails_...  (activity_id => staffs.id)
#  fk_rails_...  (employee_id => employees.id)

カラム名がactivity_idなのに、参照先がstaffsテーブルだ!!:eyes:

問題の原因

原因を探るためmigrationファイルをたどっていくと。。。。
▼こちらのマイグレーションの後に、

migration1
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

カラム名間違えたわ!!!…と
▼こんなマイグレーションを作っていました

migration2
class RenameStaffIdColumnToActivityEmployees < ActiveRecord::Migration[5.2]
  def change
    rename_column :activity_employees, :staff_id, :activity_id
  end
end

その結果、カラム名は変更できたのですが、foreign_keyは変更できていなかったようです。

解決策

上記を踏まえて、以下のようなマイグレーションを作成し、実行することで解決しました。

migration3
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を変更したことにはならないのね^^;

マイグレーションファイルを作るときのカラム名には、今後はより一層の注意を払おうと思います:cry:

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?