th_9plus
@th_9plus (たかちゃん)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

ruby on rails コメント実装時ログインに飛んでしまう

コメント時にログインに飛んでしまいます。
routesを色々調べていじりましたが結果は変わらなかったので分かる方教えて頂きたいです。

よろしくお願い致します。
スクリーンショット 2020-09-17 15.51.24.png
「コメントする」を押すとこうなります。
スクリーンショット 2020-09-17 15.51.34.png

こちらがcomment関係のコードです。

routes.rb
Rails.application.routes.draw do

  devise_for :users
  get "login" => "users#login_form"


  post "login" => "users#login"
  post "logout" => "users#logout"


  post "users/:id/update" => "users#update"
  get "users/:id/edit" => "users#edit"
  post "users/create" => "users#create"
  get "singnup" => "users#new"
  get "users/index" => "users#index"
  get "users/:id" => "users#show"

  get "posts/index" => "posts#index"
  get "posts/new" => "posts#new"
  get "posts/:id" => "posts#show"

  resources :comments, only: [:create]


  post "posts/create" => "posts#create"

  get "posts/:id/edit" => "posts#edit"

  post "posts/:id/update" => "posts#update"
  post "posts/:id/destroy" => "posts#destroy"
  get "/" => "home#top"
  get "about" => "home#about"

end
migrate/create_comments.rb
class CreateComments < ActiveRecord::Migration[6.0]
  def change
    create_table :comments do |t|
      t.string :content
      t.references :user, null: false, foreign_key: true
      t.references :post, null: false, foreign_key: true

      t.timestamps
    end
  end
end
viwe/posts/show.html.erb
<div class="main posts-show">
  <div class="container">
    <div class="posts-show-item">
      <div class="post-user-name">
        <img src="<%= "/user_images/#{@user.image_name}" %>">
        <%= link_to(@user.name, "/users/#{@user.id}") %>
      </div>
      <p>
        <%= @post.content %>
      </p>

      <div class="post-time">
       <%= @post.created_at %>
       </div>
       <% if @post.user_id == @current_user.id %>
       <div class="post-menus">
        <%= link_to("編集", "/posts/#{@post.id}/edit") %>
        <%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %>
      </div>
      <% end %>

<div class="comment-wrapper border-top mb-10">
  <p class="mt-5">コメント一覧</p>
  <% @comments.each do |c| %>
    <div>
      <% unless c.user.blank? %>
        <a href="<%= user_path(c.user_id) %>"><%= image_tag c.user.image.to_s,
          class:"rounded-circle icon_image mr-3 mb-3"%></a>
        <% end %>
      <%= c.user.username unless c.user.blank? %>
      <br />
      <%= c.content %>
    </div>
    <br />
  <% end %>
  <% if user_signed_in? %>
    <%= form_with(model: @comment, url: comments_path, method: :post, local: true) do |f| %>
      <%= f.text_area :content, class: "form-control", rows: 5 %>
      <%= button_tag type: "submit", class: "btn btn-success float-right mt-1" do %>
        <i class="far fa-comments"></i> コメントする
      <% end %>
    <% end %>
  <% end %>
</div>
    </div>
model/comment.rb
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :post
end
model/post.rb
class Post < ApplicationRecord

  validates :content, {presence: true, length: {maximum: 140}}
  validates :user_id, {presence: true}
  has_many :comments

  def user
    return User.find_by(id: self.user_id)
  end

end
model/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  validates :name, {presence: true}
  validates :email, {presence: true, uniqueness: true}
  has_many :comments

  def posts
    return Post.where(user_id: self.id)
  end
 end
controller/comments_controller.rb
class CommentsController < ApplicationController

   before_action :authenticate_user!

  def create
    post = Post.find(params[:post_id])
    @comment = post.comments.build(comment_params)
    @comment.user_id = current_user.id
    if @comment.save
      flash[:success] = "コメントしました"
      redirect_back(fallback_location: root_path)
    else
      flash[:success] = "コメントできませんでした"
      redirect_back(fallback_location: root_path)
    end
  end

  private

    def comment_params
      params.require(:comment).permit(:content)
    end

end
0

20Answer

@th_9plus config/routes.rb の get "posts/:id" => "posts#show" get "posts/:id" => "posts#show", as: :post にすれば多分動きます。

0Like

@th_9plus

ユーザーのページを表示しようとしているのに PostsController の show が実行されているのがおかしいですね。

このページではこれ以上の質問には回答しません。

0Like

すいません・・
こちらのエラーも教えて頂きませんか??
コメント一覧からコメントしているユーザーのページに移動する時に起きたエラーです。
スクリーンショット 2020-10-08 19.51.23.png
スクリーンショット 2020-10-08 19.51.33.png

0Like

@th_9plus

c.user.username の username メソッドがないエラーですね。別の場所でユーザー名を表示するところと比べてどうですか? 今度は自分で考えてみてください。

0Like

Comments

@th_9plus

エラーメッセージは中までよく読んでください。今度は undefined method image for #<User...> になっていますね。同じ NoMethodError でも出ている場所が違うので別のエラーです。 c.user.image.to_s の image メソッドが存在しないと言っています。

まず <%= image_tag ... %> のあたりを消してみて動くかどうか確かめてください。次に、他ではユーザーアイコンを正しく表示できているのになぜここでエラーになるか見比べてみてください。

<img src="<%= "/user_images/#{@user.image_name}" %>">

他ではこうなっていますね。 @userc.user に置き換えて

<img src="<%= "/user_images/#{c.user.image_name}" %>">

こうしたら動くんじゃないか……という試行錯誤をご自身でやってみてほしいです。

0Like

いつも細かくありがとうございます!
get "users/:id" => "users#show" を get "users/:id" => "users#show", as: :user に変更し
user_path 周辺を消したりしましたが同じエラーが出てます
スクリーンショット 2020-10-08 15.22.28.png
スクリーンショット 2020-10-08 15.28.34.png

0Like

いつも細かくありがとうございます!
get "users/:id" => "users#show" を get "users/:id" => "users#show", as: :user に変更し

スクリーンショット 2020-10-08 15.28.34.png

0Like

@th_9plus

get "users/:id" => "users#show"get "users/:id" => "users#show", as: :user にしてください。

さっきのエラーメッセージは undefined method post_url で今度は undefined method user_path ですね。このような _url_path で終わる URL ヘルパー(モデルを受け取って URL 文字列を返すメソッド)は config/routes.rb の定義から自動的に生成されます。

しかし get "users/:id" => "users#show" のような書き方ではうまく生成されません。ここに as: :xxx などと指示を書くと xxx_pathxxx_url が生成されるようになります。


この先も色々なところで細かいエラーが出ると思いますが、エラーが出るたび思考停止するのではなくて、重要でないエラーを飛ばして先に進められないか試してみるのも大事ですよ。

        <a href="<%= user_path(c.user_id) %>"><%= image_tag c.user.image.to_s,
          class:"rounded-circle icon_image mr-3 mb-3"%></a>

たとえば今ならこの user_path のあたりでエラーが出ていますが、これはユーザーのアイコンを表示する箇所です。コメントをするとログインに飛んでしまう問題が解決したかどうか確かめている間は、ユーザーのアイコンはさして重要ではありません。いったん user_path のあたりを消してしまって、残り全部が正しく動くかどうかを確認してみてください。

0Like

以前同じ質問をされていましたよね?(記事が消えているようですが)

そこでは ApplicationController#authenticate_user の中で session[:user_id] が nil になる問題が指摘されていたと記憶していますが、それは解消しましたか? 今の ApplicationController のコードも貼っていただけますか?

0Like

Comments

  1. @th_9plus

    Questioner

    ApplicationController#authenticate_user の中で session[:user_id] が nil になる問題が指摘されていたと記憶していますが、それは解消しましたか? 
    こちらはログインしていない状態でしたのでnilになっていました。
  2. 当時の通知を見て思い出しましたが #authenticate_user ではなく #set_current_user でした。何にしても前の質問で指摘されたことはこちらに補足してほしいものです。伏せられているページの URL も書いてほしいとか確か言われてましたよね。
  3. 結局ログインしたら nil じゃなくなったんでしたっけ。

<%= hidden_field_tag :post_id, @post.id %> をしましたが次はこちらのエラーが出てきました。
スクリーンショット 2020-10-08 11.13.36.png

0Like

Comments

  1. @th_9plus

    Questioner

    post のIDはログイン時に入力するIDと同じですよね??
  2. よく考えてみてください。今やろうとしているのは、 Comment をどの Post に紐付けるかはっきりさせるために、対象の Post の ID もフォームに入れて送信するってことですよね。 Post の ID はある Post と別の Post を区別できる値であるはずです。ログイン時に入力する ID で2つの Post を区別できますか?
  3. @th_9plus

    Questioner

    なるほど!!
    調べてみます!!
  4. @th_9plus

    Questioner

    色々調べてみましたがうまくいかずです・・・
    post_idを調べる方法はありますか?
  5. 直接の答えは存在しないので調べても出てきません。仕組みを考えて自分で書く必要があります。

    おそらくあなたは

    - Rails の基本的な理解がない
    - HTML フォームの知識がまだ足りない

    ために、自分がどこで詰まっているかも分からず、とりあえず「Rails post_id」で検索してみて答えが見つからなくて悩んでいる……というような状態なのだと思います。

    参考書なり Rails 講座なりで勉強して体系的な知識を身につけてください。
  6. 独学中とのことなので答えだけポンと教えてもためにならないかなあ……と色々ヒントを出してみたのですが、コメントではもうサポートしきれないので答えを書きます。

    <%= hidden_field_tag :post_id, @post.id %>

    これは hidden_field_tag は Rails のフォームヘルパーを使っていて、 HTML で書けば以下と同等です。

    <input type="hidden" name="post_id" value="【@post の id 】">

    これは何を意味しているのか? なぜ動くのか? @post.id とは何か? params[:post_id] との関係は? など、理解していない点に気づいたらぜひ勉強してみてください。以下のページが関連する分野です。(ただ、読んで仕組みが分かるほど親切な解説ページではないので、この分野を参考書で勉強したほうがいいと思います)

    - https://railsguides.jp/action_view_overview.html
    - https://railsguides.jp/action_controller_overview.html
    - https://railsguides.jp/active_record_basics.html
    - https://www.kanzaki.com/docs/html/htminfo31.html
  7. @th_9plus

    Questioner

    親切にありがとうございます!!
    しっかり勉強します!
  8. @th_9plus

    Questioner

    わかりました!
post = Post.find(params[:post_id])

ここでエラーが出たということは認証周りの問題は解決しましたね。エラー内容は、

  • エラーが Couldn't find post without an ID になっている
  • Resuest Parameters: { ... }post_id が入っていない

ことから判断できるように、 params[:post_id] が nil になっています。コメントの投稿フォームから post_id パラメータを送るようにしてください。

devise の導入は https://github.com/heartcombo/devise#getting-started を読んでください。

0Like

Comments

  1. @th_9plus

    Questioner

    調べたのですがコメント投稿フォームのどこあたりにparams[:post_id]を
    入れればいいのか分からなです、、、
  2. フォームに `<input type="hidden" value="PostのID">` タグを加えればいいです。そのためには Rails の `f.hidden_field` ヘルパーが使えます。
  3. これでピンとこないようでしたら Rails ビューと HTML フォームの勉強をしてください。そうでなければ何一つ自分で書けるようにならないと思います。

before_action :authenticate_user に直しましたがこのようなエラーがおきました
スクリーンショット 2020-10-03 18.02.59.png
またdeviseで一本化したいのですがどこを直せば良いでしょうか?

0Like

devise の #user_signed_in? は以下のように定義されていて、 devise でログインしていなくても自前のログイン機構によって @current_user がセットされていれば真を返す。 CommentsController では @current_user をセットせず devise だけを使っているためログインしていない扱いになる。ということのようです。

def user_signed_in?
  !!current_user
end

def current_user
  @current_user ||= warden.authenticate(scope: :user)
end

CommentsControllerbefore_action :authenticate_user!before_action :authenticate_user に直す(最後のビックリマークを消す)と動くようになると思います。……が、今のままではログイン周りで他にも問題が起きそうです。 devise か自前のログイン機構かどちらかに一本化することを強くお勧めします。

0Like

よろしくお願いします!

posts_controller.rb
class PostsController < ApplicationController
    before_action :authenticate_user
    before_action :ensure_correct_user, {only: [:edit, :update, :destroy]}
    
  def index
    @posts = Post.all.order(created_at: :desc)
  end
  
  def show
    @post = Post.find_by(id: params[:id])
    @user = @post.user
    @post = Post.find(params[:id])
    @comments = @post.comments
    @comment = @comments.new
  end
   
  def new
      @post = Post.new
  end
  
  def create
    @post = Post.new(
      content: params[:content],
      user_id: @current_user.id
      )
    if @post.save
      flash[:notice] = "投稿を作成しました"
     redirect_to("/posts/index")
    else
     render("posts/new")
    end
  end

 def edit
  @post = Post.find_by(id: params[:id])
 end
 
 def update
 @post = Post.find_by(id: params[:id])
 @post.content = params[:content]
 if @post.save
   flash[:notice] = "投稿を編集しました"
   redirect_to("/posts/index")
 else
   render("posts/edit")
 end
 end
 
 def destroy
   @post = Post.find_by(id: params[:id])
   @post.destroy
   flash[:notice] = "投稿を削除しました"
   redirect_to("/posts/index")
 end
 
 def ensure_correct_user
     @post = Post.find_by(id: params[:id])
     if @post.user_id != @current_user.id
         flash[:notice] = "権限がありません"
         redirect_to("/posts/index")
     end
 end
 
end
users_controller.rb
class UsersController < ApplicationController
  
  before_action :authenticate_user, {only: [:index, :show, :edit, :update]}
  before_action :forbid_login_user, {only: [:new, :create, :login_form, :login]}
  before_action :ensure_correct_user, {only: [:edit, :update]}
  

  def index
    @users = User.all
  end
  
  def show
    @user = User.find_by(id: params[:id])
  end
  
  def new
    @user = User.new
  end
  
  def create
    @user = User.new(
      name: params[:name],
      email: params[:email],
      image_name: "fashion-985556_1920.jpg",
      password: params[:password]
    )
    if @user.save
      session[:user_id] = @user.id
      flash[:notice] = "ユーザー登録が完了しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/new")
    end
    
  end
 
  def edit
    @user = User.find_by(id: params[:id])
  end
  
  def update
    @user = User.find_by(id: params[:id])
    @user.name = params[:name]
    @user.email = params[:email]
    
    if params[:image]
      @user.image_name = "#{@user.id}.jpg"
      image = params[:image]
      File.binwrite("public/user_images/#{@user.image_name}", image.read)
    end
    
    if @user.save
      flash[:notice] = "ユーザー情報を編集しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/edit")
    end
  end
  
  def login_from
  end
 
  
  def login
    @user = User.find_by(email: params[:email])
    if @user && @user.valid_password?(params[:password])
      session[:user_id] = @user.id
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      @error_message = "メールアドレスまたはパスワードが間違っています"
      @email = params[:email]
      @password = params[:password]
      render("users/login_form")
    end
  end
  
  

  
  def auth_failure
    @user = User.new
    render("users/login_form")
  end
  
  def logout
    session[:user_id] = nil
    flash[:notice] = "ログアウトしました"
    redirect_to("/login")
  end
  
  def ensure_correct_user
    if @current_user.id != params[:id].to_i
      flash[:notice] = "権限がありません"
      redirect_to("/posts/index")
    end
  end
  
 
end
0Like

どうも devise のログイン機構と自前のログイン機構が混在しているようですね。

  • devise
    • ApplicationController#authenticate_user!, #current_user, #user_signed_in?
    • GET /users/sign_in, DELETE /users/sign_out (が devise_for :users で定義されるはず)
  • 自前?
    • ApplicationController#set_current_user, #authenticate_user, @current_user
      • #authenticate_user は今貼られているコードではどこからも呼ばれていない
      • #authenticate_user がリダイレクトする先の GET /logins の route も存在しない
    • session[:user_id]
    • GET /login, POST /login, POST /logout

view/posts/show.html.erb の中で使われている user_signed_in? が真を返していることから devise ではログイン状態にありそうです。しかし CommentsControllerbefore_action :authenticate_user! で弾かれて devise のログイン画面にリダイレクトされているように見えるのが謎です。

PostsController#user_signed_in? が独自に定義されていて自前のログイン状態を返しているならあり得るかも。 PostsController と、ついでに UsersController のコードも貼ってもらえますか?

0Like

以前の物は間違えて消してしまいました、、、
よろしくお願い致します。

application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_current_user
  
  def set_current_user
    @current_user = User.find_by(id: session[:user_id])
    
  end
  
  def authenticate_user
    if @current_user == nil
      flash[:notice] = "ログインが必要です"
      redirect_to("/logins")
    end
  end
  
  def forbid_login_user
    if @current_user
      flash[:notice] = "すでにログインしています"
      redirect_to("/posts/index")
    end
  end
  
end
0Like

Your answer might help someone💌