Help us understand the problem. What is going on with this article?

Ruby on Rails で多対多のモデルを構築した場合にハマったエラー

More than 3 years have passed since last update.

はじめに

参考書やネットの情報を参考に多対多のモデルを構築した所、謎のエラーに遭遇し、なかなか解決できなかったので、ここに情報を残しておきます。

簡単にいうと、UserとProjectモデル + 多対多実現用のUserProjectテーブルでデータのやり取りをしようとしていたのですが、以下のエラーに阻まれましたので、その解決法です。

ActiveRecord::UnknownAttributeError: unknown attribute 'user_id' for UserProject.

環境

・Mac OS X EI Capitan (10.11.5)
・Ruby 2.3.0
・Rails 4.2.6

エラーのはじまり

Model

以下の多対多のモデルを構築しました
・User
・Project
多対多のモデルを実現するために、以下の中間テーブルも構築しました
・UserProject

モデル生成

ユーザー情報を持ったUserモデルと、簡単なプロジェクト情報を持ったProjectモデルを生成し、データベースを構築します。

$ rails g model User name email password_diggest
$ rails g model Project name body:text
$ rails g model UserProject User:reference Project:reference
$ rake db:migrate

ここまでは、全く問題なしです。
多対多の場合、user_project.rbbelongs_to が自動的に明記されますが、has_many は手動で書かないといけないみたいなので、それぞれのモデルに追記します。

user.rb
class User < ActiveRecord::Base
    # DB relations
    has_many :UserProject  # 追記
    has_many :Project, through: :UserProject  # 追記
end
project.rb
class Project < ActiveRecord::Base
    # DB relations
    has_many :UserProject  # 追記
    has_many :User, through: :UserProject  # 追記
end

追記していませんが、一応、中間テーブルはこちら

user_project.rb
class UserProject < ActiveRecord::Base
  belongs_to :User
  belongs_to :Project
end

中間テーブルに情報がわたらない

アプリケーションを作っている途中で、保存したProjectの情報がユーザーと結びついていないことに気付く。
ちなみに、ProjectのControllerは以下の通り。

projects_controller.rb
class ProjectsController < ApplicationController
  def new
    @project = Project.new()
  end

  def create
    # セッションで管理しており、current_userでログイン中のユーザーを管理
    @user = current_user
    # ログイン中のユーザーにプロジェクト情報を追加
    @project = @user.Project.new(params_project)
    if @project.save
      # DB保存成功時の処理
    else
      # DB保存失敗時の処理
    end
  end

  private

  def params_project
    params.require(:project).permit(:name, :body)
  end
end

コンソールで確認

とりあえず、railsのコンソールで確認してみる
※ pry をインストールしていない人は 補足:pryのインストール を参照

まず、railsのコンソールを立ち上げる

$ rails c

モデル間でが正しく参照しているか確認する。

[1] pry(main)> show-models
Project
  id: integer
  name: string
  body: text
  created_at: datetime
  updated_at: datetime
  has_many :User (through :UserProject)
  has_many :UserProject (foreign_key :Project_id)
User
  id: integer
  name: string
  email: string
  password_digest: string
  created_at: datetime
  updated_at: datetime
  has_many :Project (through :UserProject)
  has_many :UserProject (foreign_key :User_id)
UserProject
  id: integer
  User_id: integer
  Project_id: integer
  created_at: datetime
  updated_at: datetime
  belongs_to :Project
  belongs_to :User

うん。悪くない。
というわけで、適当にユーザーを作って、そこに、プロジェクトを紐付けてみる。

user = User.create(name: "user1")
project = Project.create(name: "project1")
user.Project << project

ここで、DBへの書き込みが失敗し、以下のエラーに遭遇

ActiveRecord::UnknownAttributeError: unknown attribute 'user_id' for UserProject.

user_id という未定義のパラメータが渡っているようですね。
あれ? User_id が渡って欲しいんだけど。。。
ここから、色々と試行錯誤して時間を費やしましたが、結果的には、非常に簡単に解決したので、ご安心ください。

エラー対処

結論からいうと、中間テーブルが参照する際の外部キーを変更してやらないとだめみたいでした。
というわけで、それぞれのモデルに外部キーを明記しました。

user.rb
class User < ActiveRecord::Base
    # DB relations
    has_many :UserProject, foreign_key: 'User_id'  # foreign_keyを追記
    has_many :Project, through: :UserProject
end
project.rb
class Project < ActiveRecord::Base
    # DB relations
    has_many :UserProject, foreign_key: 'Project_id' # foreign_keyを追記
    has_many :User, through: :UserProject
end

これで無事解決しました。

補足

pryのインストール

rails のコンソールで、いろいろと綺麗に表示できる優れものの pry をインストールします。

以下をGemfileに追記する

Gemfile
gem 'pry-rails'
gem 'pry-doc'

あとは、インストールするだけ

$ bundle install

いつもどおり、rails のコンソールを立ち上げると、pryが適用されています。

$ rails c
[1] pry(main)>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした