Ruby on Rails - Settting up Reviews functionality

折月煮酒 提交于 2019-12-08 04:57:23

问题


I am trying to set up a feature on my Ruby on Rails app that lets users to review pictures.

I've followed this guide as a reference.

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

From my experiences working on other Ruby on Rails projects, I think a Posts/Comments relationship model can be used here for a Pictures/Reviews relationship.

I first generated a scaffold.

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

I would like each picture page to have a separate page for its own reviews.

Since I don't need an Index Page for my reviews controller, I removed this line from my routes.rb file

resources: reviews

I replaced that by creating routes

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'

Here, my path involves nesting reviews inside pictures.

Route

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

I deleted the index action from my ReviewsController.

Models

class Review < ActiveRecord::Base
  belongs_to :picture
end

class Picture < ActiveRecord::Base
  has_many :reviews
end

Above, I established a one-to-many relationship between pictures and reviews.

Reviews Migration

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

Based on my understanding on Rails, this should work.

Pictures#Show Page

<% @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>

However, when I click on a link in my Pictures/:id(show page)

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

RecordNotFound Error

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

Since I encountered a RecordNotFound error, I suspect the problem lies in the ReviewsController, most likely with the params.

I believe that I have the right idea, but I made a mistake somewhere. Feedback and criticism are greatly appreciated. Sorry if this sounds like a stupid question, I'm just not very good in Ruby.


回答1:


Routes

For the sake of posterity, you'll be best setting your routes up as follows:

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

Whenever you create routes in Rails, you need to remember the entire framework has been built around "objects" / "resources". That's why the routes are known as resourceful routes (and why they have the resources directive) - they allow you to define the routes around the different resources of your application

I've recommend using a nested resources structure.

--

Helpers

Your problem was resolved using the code provided by Santosh and the like. IE:

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

You need to appreciate how this works. Each time you use a route helper in Rails (in the link_to helper), it will look through your routes & find the details it needs.

You were referencing the following path: pictures/:id/reviews - as discovered, this is wrong because Rails does not have any bearing on the URL of a link besides building it at render time.

Considering Rails is a stateless HTTP-based framework, Rails has to collate any data upon each request. This means if you want to build links, you have to let Rails build the link at render, passing a static set of data through to your controller in the back-end

Hopefully this helps?




回答2:


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

These 2 routes do not have a review id, but your controller action expects them. make this change

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

And change this

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

to

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

But you should seriously consider using Resourceful routes and helpers




回答3:


As you have .html.erb file, try to avoid writing raw HTML.

Just update this link:

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

To

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

Edit:

You should assign names to your match statements like:

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

It will generate:

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



回答4:


Consider using nested resources rather than building them out manually

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

And as others mentioned, you should use the rails link_to to generate proper URLs



来源:https://stackoverflow.com/questions/25107038/ruby-on-rails-settting-up-reviews-functionality

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!