Ruby on Rails - 设置评论功能

时间:2014-08-03 17:08:37

标签: ruby-on-rails ruby orm associations scaffolding

我正在尝试在我的Ruby on Rails应用程序上设置一个功能,让用户可以查看图片。

我已将此指南作为参考。

http://ruby.about.com/od/rubyonrails/ss/blogpart4_4.htm

根据我在其他Ruby on Rails项目上的经验,我认为Posts / Comments关系模型可以在这里用于图片/评论关系。

我首先制作了一个脚手架。

rails g scaffold review name:string body:text picture:references

我希望每个图片页面都有一个单独的页面用于自己的评论。

由于我的评论控制器不需要索引页面,因此我从routes.rb文件中删除了该行

resources: reviews

我通过创建路线替换了

match '/pictures/:id/reviews', to: 'reviews#show', via: 'get'
match '/pictures/:id/reviews/edit', to: 'reviews#edit', via: 'get'
match '/pictures/:id/reviews/new', to: 'reviews#new', via: 'get'

在这里,我的路径涉及在图片中嵌套评论。

路线

favorite_picture_path   PUT     /pictures/:id/favorite(.:format)    pictures#favorite
pictures_path           GET     /pictures(.:format)                 pictures#index
                        POST    /pictures(.:format)                 pictures#create
new_picture_path        GET     /pictures/new(.:format)             pictures#new
edit_picture_path       GET     /pictures/:id/edit(.:format)        pictures#edit
picture_path            GET     /pictures/:id(.:format)             pictures#show
                        PATCH   /pictures/:id(.:format)             pictures#update
                        PUT     /pictures/:id(.:format)             pictures#update
                        DELETE  /pictures/:id(.:format)             pictures#destroy
users_path              GET     /users(.:format)                    users#index
                        POST    /users(.:format)                    users#create
new_user_path           GET     /users/new(.:format)                users#new
edit_user_path          GET     /users/:id/edit(.:format)           users#edit
user_path               GET     /users/:id(.:format)                users#show
                        PATCH   /users/:id(.:format)                users#update
                        PUT     /users/:id(.:format)                users#update
                        DELETE  /users/:id(.:format)                users#destroy
sessions_path           POST    /sessions(.:format)                 sessions#create
new_session_path        GET     /sessions/new(.:format)             sessions#new
session_path            DELETE  /sessions/:id(.:format)             sessions#destroy
contacts_path           POST    /contacts(.:format)                 contacts#create
new_contact_path        GET     /contacts/new(.:format)             contacts#new
root_path               GET     /                                   pictures#welcome
users_new_path          GET     /users/new(.:format)                users#new
about_path              GET     /about(.:format)                    pictures#about
                        GET     /contacts(.:format)                 contacts#new
                        GET     /users/:id/favorites(.:format)      users#favorites
signup_path             GET     /signup(.:format)                   users#new
signin_path             GET     /signin(.:format)                   sessions#new
signout_path            DELETE  /signout(.:format)                  sessions#destroy
                        GET     /pictures/:id/reviews(.:format)     reviews#show
                        GET     /pictures/:id/reviews/edit(.:format)    reviews#edit
                        GET     /pictures/:id/reviews/new(.:format) reviews#new
updated_path            GET     /updated(.:format)              pictures#newest_updates
                        GET     /top-rated(.:format)            pictures#high_ratings

ReviewsController

class ReviewsController < ApplicationController
  before_action :set_review, only: [:show, :edit, :update, :destroy]

  def show
    @picture = Picture.find(params[:id])
    @review = Review.find(params[:id])
  end

  def new
    @review = Review.new
  end

  def edit
     @picture = Picture.find(params[:picture_id])
     @review = Review.find(params[:id])
  end

  def create
    @picture = Picture.find(params[:picture_id])
    @review = @picture.reviews.build(params[:review])

    if @review.save
      ;flash[:notice] = 'Review was successfully created.'
      redirect_to @picture
    else
      flash[:notice] = "Error creating review: #{@review.errors}"
      redirect_to @picture
    end
  end

  def update
    @picture = Picture.find(params[:picture_id])
    @review = Review.find(params[:id])

    if @review.update_attributes(params[:review])
      flash[:notice] = "Review updated"
      redirect_to @picture
    else
      flash[:error] = "There was an error updating your review"
      redirect_to @picture
    end
  end

  def destroy
    @picture = Picture.find(params[:picture_id])
    @review = Review.find(params[:id])
    @review.destroy
    redirect_to(@review.post)
  end

  private

    def set_review
      @review = Review.find(params[:id])
    end

    def review_params
      params.require(:review).permit(:username, :body, :picture_id)
    end
end

我从我的ReviewsController中删除了索引操作。

模型

class Review < ActiveRecord::Base
  belongs_to :picture
end

class Picture < ActiveRecord::Base
  has_many :reviews
end

上面,我在图片和评论之间建立了一对多的关系。

评论迁移

class CreateReviews < ActiveRecord::Migration
  def change
    create_table :reviews do |t|
      t.string :username
      t.text :body
      t.references :picture, index: true

      t.timestamps
    end
  end
end

根据我对Rails的理解,这应该可行。

图片#显示页面

<% @title = "#{@picture.title}" %>

<h4 class = 'indent'>Picture Statistics</h4>

  <ul id = 'view'>
    <li><strong>Title:</strong> <%= @picture.title %></li>
    <li><strong>Category:</strong> <%= @picture.category %></li>
    <li><strong>Rating:</strong> <%= pluralize(@picture.rating, 'Star') %></li>
    <li><strong>Favorited:</strong> By <%= pluralize(@picture.users.count, 'User') %></li></br>
  </ul>

  <% if @picture.rating > 4 %>

  <button class = 'top-picture'>Top Rated</button>

  <% end %>

<%= form_for @picture do |f| %>

  <p>
    <%= f.label :stars, 'Rating', class: 'indent' %>
    <div class= "rating">
      1 &#9734;<%= f.radio_button :stars, '1' %>
      2 &#9734;<%= f.radio_button :stars, '2' %>
      3 &#9734;<%= f.radio_button :stars, '3' %>
      4 &#9734;<%= f.radio_button :stars, '4' %>
      5 &#9734;<%= f.radio_button :stars, '5' %>
    </div>
  </p>

  <p class = 'indent'>
   <input class="btn btn-info" type="submit" value="Review">
  </p>

  <a href = "/pictures/:id/reviews">Reviews</a>

<% end %>
<p class = 'indent'>
  <a class="btn btn-info" href="/pictures" role="button">Index</a>
</p>

但是,当我点击我的图片/:id(显示页面)

中的链接时
<a href = "/pictures/:id/reviews">Reviews</a>

RecordNotFound错误

Active Record::RecordNotFound in ReviewsController#show
Couldn't find Review with id=:id

Extracted source (around line #54):
53 def set_review
54  @review = Review.find(params[:id])
55 end
56
57 def review_params

由于我遇到了RecordNotFound错误,我怀疑问题在于ReviewController,很可能是params。

我相信我有正确的想法,但我在某处犯了一个错误。非常感谢反馈和批评。对不起,如果这听起来像个愚蠢的问题,我对Ruby的态度不是很好。

4 个答案:

答案 0 :(得分:2)

match '/pictures/:id/reviews', to: 'reviews#show', via: 'get'
match '/pictures/:id/reviews/edit', to: 'reviews#edit', via: 'get'

这两条路线没有评论ID,但您的控制器操作需要它们。进行此更改

match '/pictures/:picture_id/reviews/:id', to: 'reviews#show', via: 'get'
match '/pictures/:picture_id/reviews/:id/edit', to: 'reviews#edit', via: 'get'

并改变这个

<a href = "/pictures/:id/reviews">Reviews</a>

<a href = "/pictures/#{@picture.id}/reviews/#{@review.id}">Reviews</a>

但您应该认真考虑使用Resourceful routeshelpers

答案 1 :(得分:2)

如果你有.html.erb文件,请尽量避免编写原始HTML。

只需更新此链接:

<a href = "/pictures/:id/reviews">Reviews</a>

<%= link_to "Reviews", picture_reviews_path(@picture) %>

修改

您应该为match语句分配名称,例如:

match '/pictures/:id/reviews', to: 'reviews#show', via: 'get', :as => 'picture_reviews'

它将生成:

picture_reviews GET    /pictures/:id/reviews(.:format)          reviews#show

答案 2 :(得分:2)

<强>路线

为了子孙后代,您最好按照以下方式设置路线:

#config/routes.rb
resources :pictures do
   resources :reviews, only: [:show, :edit, :new]
end

每当你在Rails中创建路由时,你需要记住整个框架是围绕"objects" / "resources"构建的。这就是为什么路由被称为resourceful路由(以及为什么它们具有resources指令) - 它们允许您定义路径围绕不同的资源你的申请

我建议使用nested resources结构。

-

<强>助手

使用Santosh等提供的代码解决了您的问题。 IE:

<%= link_to "Your Link", your_link_path(@object) %>

您需要了解 这是如何运作的。每次在Rails中使用route助手时(在link_to帮助器中),它都会查看您的路线&amp;找到它需要的细节。

您正在引用以下路径:pictures/:id/reviews - 如发现,这是错误的,因为除了在渲染时构建链接之外,Rails对链接的URL没有任何影响。

考虑到Rails是stateless HTTP-based framework,Rails必须在每次请求时整理任何数据。这意味着如果你想构建链接,你必须让Rails在渲染时构建链接,将一组静态数据传递给后端的控制器

希望这有帮助吗?

答案 3 :(得分:1)

考虑使用嵌套资源而不是手动构建它们

resources :pictures, except: [:index] do
  resources :reviews
end

正如其他人提到的,你应该使用rails link_to来生成正确的URL