rails嵌套资源:控制器和表单

时间:2016-09-21 17:40:17

标签: ruby-on-rails

我正在构建一个有gymsreviews的rails网站。我希望用户能够留下健身房的评论。我把我的表设置为

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 / info。我正在尝试添加评论,但我不想混淆关联。这是我的健身房控制器信息

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这是一个索引页面。我觉得有更好的方法来做到这一点。有谁看到我在这里做错了什么?

2 个答案:

答案 0 :(得分:1)

看起来你正试图从健身房控制器创建一个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

请记住,gym has_many reviews - 因此您将尽可能使用复数reviews

不确定我是否抓住了所有内容,但我建议您查看Nested Forms第9.2节中的Rails指南,以获得非常好的解释。嵌套表单可能很棘手,另一个更简单的选择是在评论控制器中创建一个单独的审阅表单(请参阅@ max&#39; s)

答案 1 :(得分:1)

首先从控制台运行$ rake routes。这将告诉您POST /gyms/:gym_id/reviews将由ReviewsController而不是GymsController处理。

这应该是应该的,因为每个控制器应该只负责CRUD的单个资源。

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