轨嵌套的资源:控制器,并形成

问题描述:

我建立一个轨道的网站,是有gymsreviews。我希望用户能够为健身房留下评论。我有我的表设置为轨嵌套的资源:控制器,并形成

class Gym < ActiveRecord::Base 
    has_many :pictures, as: :imageable 
    has_many :reviews 
end 

class Review < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :gym 
    validates :body, presence: true, length: { maximum: 1000 } 
    validates :rating, presence: true 
end 

眼下健身房控制器是静态的(不能CRUD健身房,因为这是一个管理的事情),只是呈现的页面W /信息。我正在尝试添加评论,但我不想混淆关联。这里是我的健身房控制器信息

class GymsController < ApplicationController 
    before_action :logged_in_user, only: [:index, :edit, :update, :destroy] 
    before_action :correct_user, only: [:edit, :update] 

    def index 
    @q = Gym.ransack(params[:q]) 
    @gyms = @q.result 
    @other_gyms = Gym.all 
    if @gyms.to_a.count < 1 
     flash[:warning] = "No gym matched #{params[:q][:name_or_phone_number_or_city_or_zip_code_cont]}" 
    end 
    end 

    def new 
    @gym = Gym.find(params[:id]) 
    @review = @gym.review.new 
    end 

    def create 
    @gym = Gym.find(params[:id]) 
    @review = @gym.reviews.build(gym_params) 
    if @review.save 
     flash[:success] = 'Review Saved' 
     redirect_to :back 
    else 
     render 'new' 
    end 
    end 

    def show 
    @gym = Gym.find(params[:id]) 
    @reviews = @gym.reviews 
    end 

    private 
    def gym_params 
    params.require(:gym).permit(:name, :description, :address, :address_2, :zip_code, 
           :phone_number, :website_url, :city, :state, :latitude, :longitude, 
           review_attributes: [:user_id, :rating, :body, :gym_id]) 
    end 

    def logged_in_user 
    unless logged_in? 
     store_location 
     flash[:danger] = 'Please log in' 
     redirect_to login_url 
    end 
    end 

    def correct_user 
    @user = User.find(params[:id]) 
    redirect_to(root_url) unless current_user?(@user) 
    end 
end 

我的路线

resources :gyms, only: [:index, :show] do 
    resources :reviews 
end 

和健身房/显示link_to指向gyms/:id/reviews

在健身房/新我有评论表

<%= form_for [@gym, @review] do |f| %> 
    <%= f.label :rating, 'Select your rating' %> 
    <div id='ratyRating'></div><br> 

    <%= f.text_area :body, size: '100x10' %> 

    <%= f.hidden_field :user_id, value: current_user.id %> 

    <%= f.submit 'Post', class: 'btn btn-gen' %> 
<% end %> 

是行不通的。并从link_to按钮,我得到它指向gyms/:id/reviews这是一个索引页。我觉得有更好的方法来做到这一点。有没有人看到我在这里做错了?

+0

我看到一堆东西出错了,在这里你的'correct_user'过滤器获取用户来自'params [:id]'的id。 ''flash [:warning] =“没有健身房匹配#{params [:q] [:name_or_phone_number_or_city_or_zip_code_cont]}”'暴露您的用户注入漏洞,因为您正在回显参数。 – max

+0

感谢您的支持!我不知道。你能详细解释一下吗? –

+0

http://guides.rubyonrails.org/security.html#cross-site-scripting-xss – max

从控制台运行$ rake routes开始。这会告诉你,POST /gyms/:gym_id/reviewsReviewsControllerGymsController处理。

这也正是理所应当的,因为每个控制器应该只负责CRUD'ing一个单一的资源。

class ReviewsController < ApplicationController 

    before_action :set_gym! 

    # GET /gyms/:gym_id/reviews 
    def index 
    @reviews = @gym.reviews 
    end 

    # POST /gyms/:gym_id/reviews 
    def create 
    @review = @gym.reviews.new(review_params) do |r| 
     r.user = current_user 
    end 
    if @review.save 
     redirect_to @gym, success: 'Review created!' 
    else 
     render :new 
    end 
    end 

    private 
    def set_gym! 
     @gym = Gym.find(params[:gym_id]) 
    end 

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

有些事情要注意这里 - 不要通过表单传递用户ID。它使得它很容易被欺骗。而是从会话或令牌获取当前用户。

让我们创建一个局部的形式:

<%= form_for [gym, review] do |f| %> 
    <%= f.label :rating, 'Select your rating' %> 
    <%= f.text_area :body, size: '100x10' %> 
    <%= f.submit 'Post', class: 'btn btn-gen' %> 
<% end %> 

然后当需要reviews/new.html.erb视图如果审查无效时呈现:

<%= render partial: 'form', gym: @gym, review: @review %> 

然后我们还可以嵌入gyms/show.html.erb形式:

<%= render partial: 'reviews/form', gym: @gym, review: @gym.reviews.new %> 

它看起来像你正试图创建一个从健身房控制器review。这将是一个嵌套形式,这需要在你的健身房模型accepts_nested_attributes_for

class Gym < ActiveRecord::Base 
    has_many :pictures, as: :imageable 
    has_many :reviews 
    accepts_nested_attributes_for :reviews 
end 

你的形式需要与fields_for返工:

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

     <%= f.fields_for :reviews do |reviews_form| %> 

      <%= reviews_form.label :rating, 'Select your rating' %> 
      <div id='ratyRating'></div><br> 

      <%= reviews_form.text_area :body, size: '100x10' %> 

      <%= reviews_form.hidden_field :user_id, value: current_user.id %> 
     <% end %> 

    <%= f.submit 'Post', class: 'btn btn-gen' %> 
<% end %> 

你gym_params需要看起来像这样与reviews_attributes,不review_attributes

def gym_params 
    params.require(:gym).permit(:name, :description, :address, :address_2, :zip_code, 
           :phone_number, :website_url, :city, :state, :latitude, :longitude, 
           reviews_attributes: [:user_id, :rating, :body, :gym_id]) 
end 
new行动

然后,你正在创建一个新的Gym实例,你缺少的review复数当你创建一个@review比如在:

@gym = Gym.new 
@review = @gym.reviews.build 

记住,gymhas_manyreviews - 所以你要在可能时使用复数reviews

不知道如果我抓住了一切,但我会建议检查出的Rails上Nested Forms指导,部分9.2一个很好的解释。嵌套表单可能会很棘手,另一个更简单的选项是在评论控制器中创建一个单独的评论表单(请参阅@ max的答案)

+0

请注意,这里有一个很大的区别 - 只有当您需要用户能够创建时,'nested_attributes'才真正有用在同一个请求中有几件事情。在这种情况下,它将是同一用户同时创建健身房并进行审查 - 这可能不是您想要的。 – max

+0

我同意你的看法,它的用处不大,正如我在最后一句中提到的那样。哦,不妨学习一种不同的方式。 – Ren