问题
I've built a simple app that has businesses, and those businesses have many reviews. I'm using a partial _form to create/update reviews. The create action works fine, but the update action creates a new review instead of updating the review like its supposed to. Here's the relevant code (irrelevant code removed for brevity):
Code from routes.rb
:
resources :businesses do
resources :reviews
end
Code from models/business.rb
:
class Business < ActiveRecord::Base
has_many :reviews, dependent: :destroy
validates_associated :reviews
end
Code from models/review.rb
:
class Review < ActiveRecord::Base
belongs_to :business
belongs_to :user
end
Code from controllers/reviews_controller.rb
:
class ReviewsController < ApplicationController
load_and_authorize_resource
before_filter :load_business
def new
end
def index
end
def create
@review = @business.reviews.create(review_params)
@review.user_id = current_user.id
@review.reviewer = current_user.first_name + ' ' + current_user.last_name
if @review.save
redirect_to business_path(@business)
flash[:notice] = 'Review posted!'
else
redirect_to business_path(@business)
flash[:danger] = 'Your review has an error. Please double check!'
end
end
def edit
@review = @business.reviews.find(params[:id])
end
def update
@review = @business.reviews.find(params[:id])
if @review.update(params[review_params])
redirect_to businesses_path(@business)
flash[:notice] = 'Review updated!'
else
render :action => 'edit'
end
end
def destroy
@review = @business.reviews.find(params[:id])
@review.destroy
redirect_to business_path(@business)
flash[:notice] = "Review deleted."
end
private
def load_business
@business = Business.find(params[:business_id])
end
def review_params
params.require(:review).permit(:review, :rating)
end
end
Code for `reviews/_form.html.erb:
<%= simple_form_for([@business, @business.reviews.build]) do |f| %>
<%= f.error_notification %>
<p>
<%= f.input :review %>
</p>
<p>
<%= f.input :rating, collection: 1..10 %>
</p>
<p>
<%= f.submit :class => 'btn btn-primary' %>
</p>
<% end %>
The reviews are rendered in the business#show view as a partial. Code for `reviews/_review.html.erb:
<% if !review.user_id.nil? %>
<div class="well">
<p>
<strong>Reviewer:</strong>
<%= review.reviewer %>
</p>
<p>
<strong>Review:</strong>
<%= review.review %>
</p>
<p>
<strong>Rating:</strong>
<%= review.rating %>
</p>
<span class = "timestamp">
posted <%= time_ago_in_words(review.created_at) %> ago.
</span>
<% if can? :update, review %>
<span class = "timestamp">
<%= link_to "Edit", [:edit, review.business, review], class: 'btn btn-small' %>|
<%= link_to "Delete", [review.business, review], method: :delete,
data: { confirm: 'Are you sure?' } %>
</span>
<% end %>
</div>
<% end %>
Code for reviews/edit.html.erb
:
<h1>Edit Review</h1>
<%= render 'form', review: @review %>
<button type="button" class="btn btn-default"><%= link_to 'Back', businesses_path %></button>
The weird thing is, when I click on the "Edit" link in _review.html.erb
, the URL that's generated appears to be correct, for example I get http://localhost:3000/businesses/10/reviews/82/edit
. However, the _form.html.erb is empty where I would expect it to be populated with the current data for review #82. Furthermore, when I click the "Create Review" button, it creates a new review and doesn't edit review #82.
Here's the server logs after I click "Edit" for an existing review:
Started GET "/businesses/3/reviews/81/edit" for 127.0.0.1 at 2014-08-28 19:18:58 -0500
Processing by ReviewsController#edit as HTML
Parameters: {"business_id"=>"3", "id"=>"81"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 8 ORDER BY "users"."id" ASC LIMIT 1
Review Load (6.9ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."id" = $1 LIMIT 1 [["id", 81]]
Business Load (0.5ms) SELECT "businesses".* FROM "businesses" WHERE "businesses"."id" = $1 LIMIT 1 [["id", 3]]
Review Load (27.4ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."business_id" = $1 AND "reviews"."id" = $2 LIMIT 1 [["business_id", 3], ["id", 81]]
Rendered reviews/_form.html.erb (107.0ms)
Rendered reviews/edit.html.erb within layouts/application (143.4ms)
Completed 200 OK in 967ms (Views: 669.2ms | ActiveRecord: 35.6ms)
Started POST "/businesses/3/reviews" for 127.0.0.1 at 2014-08-28 19:19:23 -0500
Processing by ReviewsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"<redacted>", "review"=>{"review"=>"Edit review #81.", "rating"=>"1"}, "commit"=>"Create Review", "business_id"=>"3"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 8 ORDER BY "users"."id" ASC LIMIT 1
Business Load (0.6ms) SELECT "businesses".* FROM "businesses" WHERE "businesses"."id" = $1 LIMIT 1 [["id", 3]]
(28.9ms) BEGIN
SQL (132.9ms) INSERT INTO "reviews" ("business_id", "created_at", "rating", "review", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["business_id", 3], ["created_at", "2014-08-29 00:19:23.470750"], ["rating", 1], ["review", "Edit review #81."], ["updated_at", "2014-08-29 00:19:23.470750"]]
(39.7ms) COMMIT
(0.3ms) BEGIN
SQL (0.9ms) UPDATE "reviews" SET "reviewer" = $1, "updated_at" = $2, "user_id" = $3 WHERE "reviews"."id" = 83 [["reviewer", "<redacted>"], ["updated_at", "2014-08-29 00:19:23.692880"], ["user_id", 8]]
(17.5ms) COMMIT
Redirected to http://localhost:3000/businesses/3
Completed 302 Found in 553ms (ActiveRecord: 221.6ms)
Started GET "/businesses/3" for 127.0.0.1 at 2014-08-28 19:19:23 -0500
Processing by BusinessesController#show as HTML
Parameters: {"id"=>"3"}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = 8 ORDER BY "users"."id" ASC LIMIT 1
Business Load (0.5ms) SELECT "businesses".* FROM "businesses" WHERE "businesses"."id" = $1 LIMIT 1 [["id", 3]]
(0.5ms) SELECT AVG("reviews"."rating") AS avg_id FROM "reviews" WHERE "reviews"."business_id" = $1 [["business_id", 3]]
Rendered reviews/_form.html.erb (5.7ms)
Review Load (0.6ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."business_id" = $1 ORDER BY "reviews"."id" DESC [["business_id", 3]]
Rendered reviews/_review.html.erb (58.1ms)
Rendered businesses/show.html.erb within layouts/application (173.5ms)
Rendered users/_avatar.html.haml (13.1ms)
Rendered layouts/_navigation.html.haml (30.9ms)
Rendered layouts/_messages.html.haml (1.5ms)
Rendered layouts/_footer.html.haml (16.5ms)
Rendered layouts/_analytics.html.haml (0.6ms)
Rendered layouts/_javascripts.html.haml (0.5ms)
Completed 200 OK in 421ms (Views: 391.5ms | ActiveRecord: 2.3ms)
It's clear that the second action in the server log is "Started POST..." which isn't correct. While I'm at it, I may as well provide the relevant routes from rake routes
:
business_reviews GET /businesses/:business_id/reviews(.:format) reviews#index
POST /businesses/:business_id/reviews(.:format) reviews#create
new_business_review GET /businesses/:business_id/reviews/new(.:format) reviews#new
edit_business_review GET /businesses/:business_id/reviews/:id/edit(.:format) reviews#edit
business_review GET /businesses/:business_id/reviews/:id(.:format) reviews#show
PATCH /businesses/:business_id/reviews/:id(.:format) reviews#update
PUT /businesses/:business_id/reviews/:id(.:format) reviews#update
DELETE /businesses/:business_id/reviews/:id(.:format) reviews#destroy
I've already tried implementing every answer I could find on here, but to no avail. The closest one I've found to being almost my exact problem was this one, but it didn't help either: Rails: Use same partial for creating and editing nested items
Many thanks.
回答1:
According to your output in console, you've got a sql insert stmt before update stmt. Your code in the form cause it. @business.reviews.build
will create new review object even when edit.
Update your form_for to:
<%= simple_form_for([@business, @review]) do |f| %>
Instead of @business.reviews.build
. You should create objects in controller instead of view.
So you may need to new review objects in your new action too.
def new
@review = @business.reviews.build
end
I have some more suggestions for you to simplify your code that could make things easier.
Use review id to find review, instead of through business.
Such as code in edit and update, you could:
def edit
@review = Reviews.find(params[:id])
end
def update
@review = Reviews.find(params[:id])
...
end
来源:https://stackoverflow.com/questions/25560323/rails-4-form-partial-creates-new-record-but-wont-edit-current-record