41
50

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.

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

Last updated at Posted at 2016-07-17

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

簡単にいうと、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)>
41
50
4

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
41
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?