senseIY
@senseIY (I Y)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

user_idメソッドを使用していないのにNoMethodError: undefined method `user_id`が出る

解決したいこと

2つのモデル(dictum_user と gogaku)を関連付けたテストをパスさせたい。

rails入門者です。railsチュートリアルを参考に現在アプリ開発をしていますが、2つのモデルの関連付ける作業で詰まってしまいました。

発生している問題・エラー

➜  dictum git:(connect_gogaku_and_d_user) ✗ rails t
Running via Spring preloader in process 18575
Run options: --seed 32489

# Running:

....E

Error:
GogakuTest#test_body_should_be_present:
NoMethodError: undefined method `user_id' for #<Gogaku id: nil, subject: "programing", body: "      ", dictum_user_id: 762146111, created_at: nil, updated_at: nil>
    test/models/gogaku_test.rb:32:in `block in <class:GogakuTest>'


rails test test/models/gogaku_test.rb:30

E

Error:
GogakuTest#test_subject_should_be_present:
NoMethodError: undefined method `user_id' for #<Gogaku id: nil, subject: "      ", body: "ror", dictum_user_id: 762146111, created_at: nil, updated_at: nil>
    test/models/gogaku_test.rb:27:in `block in <class:GogakuTest>'


rails test test/models/gogaku_test.rb:25

E

Error:
GogakuTest#test_user_id_should_be_present:
NoMethodError: undefined method `user_id' for #<Gogaku id: nil, subject: "programing", body: "ror", dictum_user_id: nil, created_at: nil, updated_at: nil>
    test/models/gogaku_test.rb:22:in `block in <class:GogakuTest>'


rails test test/models/gogaku_test.rb:20

E

Error:
GogakuTest#test_micael_is_valid?:
NoMethodError: undefined method `user_id' for #<Gogaku id: nil, subject: "programing", body: "ror", dictum_user_id: 762146111, created_at: nil, updated_at: nil>
    test/models/gogaku_test.rb:13:in `block in <class:GogakuTest>'


rails test test/models/gogaku_test.rb:12

E

Error:
GogakuTest#test_should_be_valid:
NoMethodError: undefined method `user_id' for #<Gogaku id: nil, subject: "programing", body: "ror", dictum_user_id: 762146111, created_at: nil, updated_at: nil>
    test/models/gogaku_test.rb:17:in `block in <class:GogakuTest>'


rails test test/models/gogaku_test.rb:16


行ったテスト

# gogaku_test.rb

require 'test_helper'

class GogakuTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
  def setup
    @dictum_user = dictum_users(:michael)
    @gogaku = @dictum_user.gogakus.build(subject:"programing", body:"ror")
  end

  test "micael is valid?" do
    assert @dictum_user.valid?
  end

  test "should be valid" do
    assert @gogaku.valid?
  end

  test "user id should be present" do
    @gogaku.dictum_user_id = nil
    assert_not @gogaku.valid?
  end

  test "subject should be present" do
    @gogaku.subject = "      "
    assert_not @gogaku.valid?
  end

  test "body should be present" do
    @gogaku.body = "      "
    assert_not @gogaku.valid?
  end
end

test/fixtures/dictum_usres.yml

michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= DictumUser.digest('password') %>
archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= DictumUser.digest('password') %>

マイグレーションファイル

# db/migrate/20220226234019_create_dictum_users.rb (その他
# add_column :dictum_users, :password_digest, :string ,add_index :dictum_users, 
#:email, unique: true ,add_column :dictum_users, 
#:password_digest, :stringを追加してあります。)

class CreateDictumUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :dictum_users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

# db/migrate/20220301112520_create_gogakus.rb

class CreateGogakus < ActiveRecord::Migration[6.0]
  def change
    create_table :gogakus do |t|
      t.string :subject
      t.text :body

      t.references :dictum_user, type: :bigint, foreign_key: true
      t.timestamps
    end
    add_index :gogakus, [:dictum_user_id, :created_at]

    # add_foreign_key :対象のテーブル名, :指定先のテーブル
  end
end

コントローラー

# app/controllers/dictum_users_controller.rb

class DictumUsersController < ApplicationController

  before_action :logged_in_user, only: [:edit, :update, :destroy]
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy

  def show
    @dictum_user = DictumUser.find(params[:id])
  end

  def new
    @dictum_user = DictumUser.new
  end

  def create
    @dictum_user = DictumUser.new(dictum_user_params)
    if @dictum_user.save
      log_in @dictum_user
      flash[:success] = "Welcome to the DICTUM!"
      redirect_to @dictum_user
    else
      render "new"
    end
  end

  def edit
    @dictum_user = DictumUser.find(params[:id])
  end

  def update
    @dictum_user = DictumUser.find(params[:id])
    if @dictum_user.update(dictum_user_params)
      flash[:success] = "Profile updated"
      redirect_to @dictum_user
    else
      render 'edit'
    end
  end

  def destroy
    DictumUser.find(params[:id]).destroy
    flash[:success] = "Dictum_user deleted"
    redirect_to root_url
  end

    private

      def dictum_user_params
        params.require(:dictum_user).permit(:name, :email, :password,
                                    :password_confirmation)
      end

      # beforeアクション

    # ログイン済みユーザーかどうか確認
      def logged_in_user
        unless logged_in?
          store_location
          flash[:danger] = "Please log in."
          redirect_to login_url
        end
      end

      # 正しいユーザーかどうか確認
      def correct_user
        @dictum_user = DictumUser.find(params[:id])
        redirect_to(root_url) unless current_user?(@dictum_user)
      end

      # 管理者かどうか確認
      def admin_user
        redirect_to(root_url) unless current_user.admin?
      end
end

# app/controllers/gogakus_controller.rb

class GogakusController < ApplicationController
    before_action :set_gogaku, only:[:show,:edit,:update,:destroy]
    def index
        @gogakus = Gogaku.all
    end

    def show
        # @gogaku = Gogaku.find(params[:id])
    end

    def new
        @gogaku = Gogaku.new
    end

    def create
        gogaku_params = params.require(:gogaku).permit(:subject,:body,:num)
        @gogaku = Gogaku.new(gogaku_params)
        if @gogaku.save
            flash[:notice] = "教科書登録成功"
            redirect_to gogakus_path
        else
            flash.now[:alert] = "登録失敗"
            render :new
        end
    end

    def edit
        # @gogaku = Gogaku.find(params[:id])
    end

    def update
        # @gogaku = Gogaku.find(params[:id])
        gogaku_params = params.require(:gogaku).permit(:subject, :body ,:num)
        if @gogaku.update(gogaku_params)
            flash[:notice]="更新成功"
            redirect_to gogakus_path
        else
            flash.now[:alert]="更新失敗"
            render:edit
        end
    end

    def destroy
        # @gogaku = Gogaku.find(params[:id])
        @gogaku.destroy
        flash[:notice]="削除しました"
        redirect_to gogakus_path
    end

    private

        def set_gogaku
            @gogaku ||= Gogaku.find(params[:id])
        end
end

自分で試したことなど

・まず2つのモデルが正しく関連付けできているかだが、エラー文にdictum_user_id: 762146111とあるので関連付けはできているのではと考えた。(その前に親よりも先に子を作ってしまったのでエラーが出ていたが、タイムスタンプを書き換えてエラーを解決)
・エラー文ではuser_idという定義されていないメソッドがGogakuに対して呼び出されているとあるが、user_idメソッドは使用していない。test_helperには

  # テストユーザーがログイン中の場合にtrueを返す
  def is_logged_in?
    !session[:user_id].nil?
  end

  # テストユーザーとしてログインする
  def log_in_as(user)
    session[:user_id] = user.id
  end
end

class ActionDispatch::IntegrationTest

  # テストユーザーとしてログインする
  def log_in_as(user, password: 'password', remember_me: '1')
    post login_path, params: { session: { email: user.email,
                                          password: password,
                                          remember_me: remember_me } }
  end

のように記述しているがsessionで使ったコードなので関係ないはず。消したらテストのエラーが追加で増えただけだった。

・新しくインスタンスを作ったのに、id, created_at, updated_atがnilになってしまっているのが気になる。
・has_many belongs_toは記述済み
・rails入門者のため何かしら間違った認識をしているかもしれません。何かしらアドバイスがあればよろしくお願い致します。

0

1Answer

自己解決で来ました。
app/models/gogaku.rbに
validates :user_id, presence: true
という記述があったためでした。

0Like

Your answer might help someone💌