게시물에 대한 빈 댓글로 Invalid date가 나왔을 때의 대처법

13941 단어 루비Rails

목적



자작 앱으로 투고에 코멘트를 할 수 있는 기능을 작성.
그 때 나오는 에러의 해결에 시간이 걸렸기 때문에, 해결까지의 생각을 비망록으로서 기사로 했습니다.

환경



windows10 홈
우분투 20.04.1 on WSL2
rails 6.0.3
루비 2.6.6

일어난 현상



빈 주석을 보내면 Invalid date 오류가 발생합니다.


코멘트 모델의 컬럼은 이하에 기재
  • id
  • content (코멘트의 내용)
  • user_id (외부 키)
  • micropost_id (외부 키)
  • created_at
  • updated_at

  • ①뷰를 확인



    microposts/show.html.erb
    <% @comments.each do |c| %>
      <div class="d-flex"> 
        <div class="d-flex"> 
          <%= link_to user_path(c.user) do %> 
              <%= image_tag c.user.portrait.to_s, size: "40x40" %> 
          <% end %> 
        </div> 
        <div class="d-flex flex-column"> 
          <small class="text-muted"><%= c.user.name %></small> 
          <div> 
            <small><%= simple_format(h(c.content)) %></small> 
          </div> 
        </div> 
      </div> 
      <div class="d-flex flex-row mb-3 border-bottom border-dark"> 
        <div class="d-flex align-items-center"> 
          <small><%= Date.parse(c.created_at.to_s).strftime("%m月%d日") %></small>  <%# ここでエラー発生 %> 
        </div> 
        <% if c.user.id == current_user.id %> 
          <%= link_to "削除", micropost_comment_path(@micropost, c), method: :delete, data: {confirm: "コメントを削除しますか?"}, class: "btn btn-sm btn-light ml-2" %> 
        <% end %> 
      </div> 
    <% end %>
    

    일자를 「00월 000일」의 형태로 표시시키는 곳에서 에러가 발생하고 있다.
    이 메소드는 다른 페이지에서도 사용하고 있어, 그 때 에러가 나오지 않았기 때문에
    다른 부분에 버그가 있었고 여기에 걸려있는 것이 아닐까 생각했습니다.

    ②바리데이션을 확인



    우선은 코멘트 모델의 밸리데이션이 제대로 기술되어 있는지 확인한다.

    comment.rb
    class Comment < ApplicationRecord
      belongs_to :user
      belongs_to :micropost
    
      validates :content, presence: true, length: {maximum: 200}
      validates :user_id, {presence: true}
      validates :micropost_id, {presence: true}
    end
    

    모델의 밸리데이션에 문제는 없을 것 같다.
    그렇다면 데이터베이스의 NotNull 제약에 뭔가 문제가 있을지도.

    참고 : 【Rails】 「테이블의 컬럼에 정의하는 Not Null 제약」과 「모델에 정의하는 밸리데이션(presence: true)」의 거동의 차이.

    데이터베이스의 내용을 조사해 본다.
    mysql> desc comments;
    +--------------+-------------+------+-----+---------+----------------+
    | Field        | Type        | Null | Key | Default | Extra          |
    +--------------+-------------+------+-----+---------+----------------+
    | id           | bigint      | NO   | PRI | NULL    | auto_increment |
    | content      | text        | NO   |     | NULL    |                |
    | user_id      | bigint      | NO   | MUL | NULL    |                |
    | micropost_id | bigint      | NO   | MUL | NULL    |                |
    | created_at   | datetime(6) | NO   |     | NULL    |                |
    | updated_at   | datetime(6) | NO   |     | NULL    |                |
    +--------------+-------------+------+-----+---------+----------------+
    6 rows in set (0.03 sec)
    

    제대로 :content 컬럼에 NotNull 제약이 붙어 있는 것을 확인.
    아무래도 밸리데이션에 문제는 없는 것 같다.

    ③콘솔로 확인



    다음에 빈 투고가 송신되었을 때의 움직임을 콘솔에서 조사해 본다.
    pry(main)> c = Comment.new(user_id:33, micropost_id:76, content:"")
    => #<Comment:0x0000563201d95c18
     id: nil,
     content: "",
     user_id: 33,
     micropost_id: 76,
     created_at: nil,
     updated_at: nil>
    
    pry(main)> c.save!
    ROLLBACK
    ActiveRecord::RecordInvalid: バリデーションに失敗しました: Contentを入力してください
    from /home/XXXXX/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.5/lib/active_record/validations.rb:80:in `raise_validation_error'
    

    content가 빈 코멘트는 제대로 바리데이션으로 걸려 있다.
    ...응? 그럼 왜 Invalid date 에러가 되는 거야?
    빈 코멘트의 인스턴스가 어떤 이유로 create되어 있다고?

    ④ 컨트롤러를 확인



    게시 세부정보 페이지에 댓글을 표시하도록 하려면
    코멘트와 마이크로 포스트 두 개의 컨트롤러를 확인합니다.

    comments_controller.rb
    class CommentsController < ApplicationController
      before_action :authenticate_user!
      before_action :set_micropost
    
      def create
        @comments = @micropost.comments
        @comment = @micropost.comments.new(comment_params)
        @comment.user = current_user
        if @comment.save
          flash[:primary] = "コメントしました"
          redirect_to @micropost
        else
          flash.now[:danger] = "コメントを入力してください"
          render("microposts/show")
        end
      end
    ・・・
    (略)
    ・・・
      private
        def set_micropost
          @micropost = Micropost.find_by(id: params[:micropost_id])
        end
    
        def comment_params
          params.required(:comment).permit(:content).merge(user_id: current_user.id, micropost_id: params[:micropost_id])
        end
    end
    

    microposts_controller.rb
    def show
      @micropost = Micropost.find_by(id: params[:id])
      @comments = @micropost.comments
      @comment = Comment.new # コメントフォームのインスタンスはmicropost/showで作成
    end
    

    마이크로 포스트 컨트롤러에 문제는 없을 것 같다.
    그렇게 되면 코멘트 컨트롤러의 create 액션에 원인이 있을 것 같다.

    ⑤ 원인 판명



    create 액션을 보고 최초로 정의한 @comments 그리고 다음에 정의한 @comment
    @micropost.comments에 붙어 있다는 것을 깨달았다.
    create @comment는 빨리 @micropost.comments로 통합된다.

    내장 된 @comment 뷰가 표시하려고하지만,
    created_at가 nil이므로 Invalid date 오류가 발생합니다.
    @comment 을 정의할 때 @micropost

    【변경 전】

    comment_controller.rb
    ・・・
    def create
        @comments = @micropost.comments
        @comment = @micropost.comments.new(comment_params)
        @comment.user = current_user
        if @comment.save
          flash[:primary] = "コメントしました"
          redirect_to @micropost
        else
          flash.now[:danger] = "コメントを入力してください"
          render("microposts/show")
        end
      end
    ・・・
    

    【변경 후】

    comment_controller.rb
    ・・・
    def create
        @comments = @micropost.comments
        @comment = Comment.new(comment_params)  # 変更
        @comment.user = current_user
        if @comment.save
          flash[:primary] = "コメントしました"
          redirect_to @micropost
        else
          flash.now[:danger] = "コメントを入力してください"
          render("microposts/show")
        end
      end
    ・・・
    

    성공적인 성공.
    써 보면 단순한 일이지만 대단한 시간 걸렸습니다.

    좋은 웹페이지 즐겨찾기