【问题标题】:How to implement comment associated to post and user in rails 5如何在rails 5中实现与帖子和用户相关的评论
【发布时间】:2019-10-01 11:14:03
【问题描述】:

我正在为帖子实现一个 cmets,它应该 显示在帖子索引中,但不会创建评论。

我尝试了很多方法来获得解决方案但没有结果,这是 最后一个。

用户模型

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

    has_many :posts, dependent: :destroy
    has_many :likes, dependent: :destroy
    has_many :comments, dependent: :destroy

    before_save :downcase_email

    validates :name, presence: true, length: { maximum: 50 }, 
     allow_blank: false
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\. 
    [a-z]+\z/i.freeze
    validates :email, presence: true, length: { maximum: 255 },
                        format: { with: VALID_EMAIL_REGEX },
                        uniqueness: { case_sensitive: false }
    validates :password, presence: true, length: { minimum: 6 }, 
    allow_nil: false, allow_blank: false

    private

    # Converts email to all lower-case.
    def downcase_email
        self.email = email.downcase
    end
  end

后模型

  class Post < ApplicationRecord
    belongs_to :user
    has_many :likes, dependent: :destroy
    has_many :comments, dependent: :destroy

    default_scope -> { order(created_at: :desc) }
    mount_uploader :picture, PictureUploader
    validates :user_id, presence: true
    validates :content, presence: true, length: { maximum: 300 }
    validate :picture_size

    private

    # Validates the size of an uploaded picture.
    def picture_size
        errors.add(:picture, 'image size could not be more than 
    5MB') if picture.size > 5.megabytes
    end
  end

评论模型

  class Comment < ApplicationRecord
    belongs_to :user
    belongs_to :post
    validates :user_id, presence: true
    validates :body, presence: true, length: { maximum: 300 }
    validates :post_id, presence: true

    default_scope -> { order(created_at: :desc) }
  end

应用控制器

  class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception

    # to avoid error message "undefined method `errors' for 
    nil:NilClass"
    before_action :instantiate_post
    before_action :instantiate_comment
    def instantiate_post
        @post = Post.new
    end

    def instantiate_comment
        @comment = Comment.new
    end
  end

用户控制器

  class UsersController < ApplicationController
    #before_action :set_user, only: %i[show edit update destroy]
    before_action :authenticate_user!, only: %i[create destroy]
    before_action :current_user, only: %i[create destroy]


    def index
        @users = User.all
    end


    def show
        @user = User.find_by(id: params[:id])
        @posts = @user.posts
        @comments = @post.comments
        @comment = current_user.comments.build(:post => @post)
    end


    def profile
        @user = User.find_by(id: params[:id])
    end

    def destroy
        @user = User.find_by(id: params[:id])
        @user.destroy
        respond_to do |format|
        format.html { redirect_to users_path, notice: 'User was 
    successfully destroyed.' }
        format.json { head :no_content }
        end
    end

    private


        # def set_user
        #   #    @user = User.find(params[:id])
        # end

        # Never trust parameters from the scary internet, only 
    allow the white list through.
        def user_params
        params.require(:user).permit(:name, :email, :password)
        end
    end

帖子控制器

  class PostsController < ApplicationController
    before_action :set_post, only: %i[show edit update destroy]
    before_action :authenticate_user!, only: %i[create destroy]
    before_action :current_user, only: %i[create destroy]

    def index
        @posts = Post.all
    end

    def show; end

    def new
        @post = Post.new
    end

    def edit; end

    def create
        @post = current_user.posts.build(post_params)
        respond_to do |format|
        if @post.save
            format.html { redirect_to authenticated_root_path, 
        notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, 
        location: @post }
        else
            format.html { redirect_to current_user, alert: 'post 
        not created' }
            format.json { render json: @post.errors, status: 
    :unprocessable_entity }
        end
        end
    end

    def update
        respond_to do |format|
        if @post.update(post_params)
            format.html { redirect_to @post, notice: 'Post was 
       successfully updated.' }
            format.json { render :show, status: :ok, location: 
       @post }
        else
            format.html { render :edit }
            format.json { render json: @post.errors, status: 
    :unprocessable_entity }
        end
        end
    end

    def destroy
        @post.destroy
        respond_to do |format|
        format.html { redirect_to posts_url, notice: 'Post was 
    successfully destroyed.' }
        format.json { head :no_content }
        end
    end

    private

        # Use callbacks to share common setup or constraints 
       between actions.
        def set_post
        @post = Post.find(params[:id])
        end

        # Never trust parameters from the scary internet, only 
    allow the white list through.
        def post_params
        params.require(:post).permit(:content, :picture)
        end
    end

评论控制器

  class CommentsController < ApplicationController
    before_action :set_post, only: %i[update destroy]
    before_action :set_comment, only: %i[update destroy]
    before_action :authenticate_user!, only: %i[create destroy]
    before_action :current_user, only: %i[create destroy]

    def index
        @comments = Comment.all
    end

    def new
        @comment = Comment.new
    end
    def update
        respond_to do |format|
        if @comment.update(comment_params)
            format.html { redirect_to @post, notice: 'Post was 
            successfully updated.' }
            format.json { render :show, status: :ok, location: 
        @post }
        else
            format.html { render :edit }
            format.json { render json: @post.errors, status: 
    :unprocessable_entity }
        end
        end
    end

    def destroy
        @comment.destroy
        respond_to do |format|
        format.html { redirect_to posts_url, notice: 'Post was 
    successfully destroyed.' }
        format.json { head :no_content }
        end
    end

    def create
        @comment = @post.comments.build(comment_params)
        @comment.user = current_user
        respond_to do |format|
        if @comment.save
            format.html { redirect_to @post, notice: 'Post was 
      successfully commented.' }
            format.json { render :show, status: :created, 
        location: @post }
        else
            format.html { redirect_to @post, alert: 'post not 
       commented' }
            format.json { render json: @post.errors, status: 
    :unprocessable_entity }
        end
        end
    end

    private

        def comment_params
        params.require(:comment).permit(:body, :post_id)
        end

        def set_post
        @post = Post.find(params[:post_id])
        end

        def set_comment
          @comment = @post.comments.find(params[:id])
        end
    end

浏览量/帖子/索引

  <div class="row">
    <p id="notice"><%= notice %></p>  
    <div class="col-md-6 form-new-user col-md-offset-3">    
        <h1>Posts</h1> 
        <%=render "form",post: @post =%> <br>
        <% @posts.each do |post| %>
        <ol class='posts'>
            <%= render post =%>

            <p>Comment on This Post</p>
            <%= render "comments/form" ,post: post=%>
            <%= render @post.comments %>
        </ol>
        <% end %>
    </div>
  </div>

views/cmets/_comment.html.erb

  <li id="comment-<%= comment.id %>">
    <%= link_to gravatar_for(comment.user, size: 30), comment.user 
    %>
    <span class="user"><%= link_to comment.user.name, comment.user 
    %></span>
    <span class="content">
        <%= comment.body %>
        <%= image_tag post.picture.url if post.picture? %>
    </span>
    <span class="timestamp">
        At: <%= (comment.created_at) %> 
        <% if current_user == post.user || current_user == 
        comment.user %>
        <%= link_to 'Edit', edit_post_path(post) %> |
        <%= link_to "delete", post, method: :delete,
                                        data: { confirm: "You 
    sure?" } %>
        <% end %>
    </span>
  </li>

views/cmets/_form.html.erb

  <%= form_for [post, Comment.new] do |f| %>
    <%= f.hidden_field :post_id, value: post.id %>
    <%= f.label :body %><br>
    <%= f.text_area :body ,class:"form-control"%><br>
    <%= f.submit "Replay", class: "btn btn-primary" %>
  <% end %>

Routes.rb

  Rails.application.routes.draw do
    resources :posts do
        resources :comments
    end

    # For details on the DSL available within this file, see 
    http://guides.rubyonrails.org/routing.html

    devise_for :users, controllers: { registrations: 
   'users/registrations' }

    as :user do
        authenticated :user do
        root 'posts#index', as: :authenticated_root
        end

        unauthenticated do
        root 'devise/registrations#new', as: :unauthenticated_root
        end
    end
    resources :users, only: %i[index show destroy]
    get '/users/:id/profile', to: 'users#profile', as: 'profile'

  end

获得回滚

  Started POST "/posts/250/comments" for ::1 at 2019-10-01 
   12:46:45 +0100
   Processing by CommentsController#create as HTML
   Parameters: {"utf8"=>"✓", 
   "authenticity_token"=>"KNntIHqHzAQ9RWylAUoXa+NmEvMTL
   ZaewL2Z3wrO0ZB921j1YGjYJA6OAXVjDCQ1GNuN0Okv4tuSp4BacDXwqA=="
   ,"comment"=>{"post_id"=>"250", "body"=>"fixed?"}, 
   "commit"=>"Replay", "post_id"=>"250"}
   User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE 
   "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  
   [["id", 1], ["LIMIT", 1]]
   ↳/home/othmane/.rbenv/versions/2.6.1/lib/ruby/gems/
    2.6.0/gems/activerecord- 
    5.2.3/lib/active_record/log_subscriber.rb:98
    (0.2ms)  BEGIN
    ↳ app/controllers/comments_controller.rb:40
    (2.7ms)  ROLLBACK
    ↳ app/controllers/comments_controller.rb:40
     Redirected to http://localhost:3000/posts
    Completed 302 Found in 22ms (ActiveRecord: 3.6ms)

【问题讨论】:

    标签: ruby model-view-controller ruby-on-rails-5


    【解决方案1】:

    服务器告诉你post_id 是一个不允许的参数。我们还可以看到您正在验证 post_id 是否存在于任何保存的 cmets 上。我想评论未通过此验证,导致回滚。

    要允许 post_id 和正文,请将 cmets 控制器的私有部分更改为:

    private
    
      def comment_params
        params.require(:comment).permit(:body, :post_id)
      end
    
      def set_post
        @post = Post.find(params[:post_id])
      end
    
      def set_comment
        @comment = @post.comments.find(params[:id])
      end
    end
    

    这应该允许评论与帖子相关联,并通过验证。

    【讨论】:

    • 是的,已修复,但总是有回滚,这次没有权限问题。
    • 请将您在 cmets 控制器的创建操作中的行从 if comment.save 更改为 if comment.save!
    • 不保存会导致app报错,并在你的服务器日志中输出原因
    • 浏览器显示Validation failed: Post can't be blank
    • 我看不出有任何理由说明上述代码不能与服务器日志中的参数一起使用。我在这里唯一的建议是明确设置评论的 post_id
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 2023-04-10
    相关资源
    最近更新 更多