Rails-通过表格插入has_many中

时间:2018-08-17 19:35:43

标签: ruby-on-rails has-many-through

我有两个类,用户和机会,它们利用具有has_many:through关系的联接表来允许用户注册一个或多个机会(并允许一个机会让许多用户注册)。

class User < ApplicationRecord
  has_many :opportunity_enrolments, :class_name => 'OpportunityEnrolment', foreign_key: "user_id"
  has_many :opportunities, through: :opportunity_enrolments, foreign_key: "opportunity_id"
  has_secure_password
  validates :email, presence: true, uniqueness: true
end
class Opportunity < ApplicationRecord
  has_many :opportunity_enrolments, :class_name => 'OpportunityEnrolment'
  has_many :users, through: :opportunity_enrolments
end
class OpportunityEnrolment < ApplicationRecord
  belongs_to :opportunity
  belongs_to :user
end

仅当用户和机会同时存在时,用户才会注册机会。因此,注册功能将在用户和机会的创建之外进行,它们分别发生并且可以正常工作。我以为我可以在查看机会时使用OpportunityEnrolment.create方法,使用下面的表单和控制器将新行添加到机会表中。

opportunity / show.html.erb

<%= form_with(model: OpportunityEnrolment.create, local: true) do |form| %>
<p>
  <strong>Name:</strong>
  <%= @opportunity.voppname %>
</p>
<p>
  <strong>Shortdescr:</strong>
  <%= @opportunity.shortdescr %>
</p>
<p>
  <%= form.submit %>
</p>
<% end %>

opportunity_enrolments_controller.rb

def create      
   @opportunity_enrolment = OpportunityEnrolment.new(user_id: current_user.id, opportunity_id: @opportunity.id)

  error checking and .save...
end

但是,我表单中的@ opportunity.id并未传递给OpportunityEnrolment.create方法,因此提交时出现“未定义方法id为nil:NilClass的错误”错误。我尝试了另一种方式,在表单中有一个隐藏字段(了解其不安全),并且该对象仍未通过,但是我遇到了Undefined method错误。

我如何将对象信息(机会)传递给另一个类(OpportunityEnrolments),以便可以将一行添加到机会表中?

谢谢

2 个答案:

答案 0 :(得分:1)

如果您使用隐藏字段,则需要在hopport_enrolments_controller中的create操作中实例化机会。

def create
  @opportunity = Opportunity.find(params[:opportunity_id])
  @opportunity_enrolment = OpportunityEnrolment.new(user_id: current_user.id, opportunity_id: @opportunity.id)
end

形式:

<%= form_with(model: OpportunityEnrolment.create, local: true) do |form| %>
<p>
  <strong>Name:</strong>
  <%= @opportunity.voppname %>
</p>
<p>
  <strong>Shortdescr:</strong>
  <%= @opportunity.shortdescr %>
  <%= form.hidden_field :opportunity_id, value: @opportunity.id %>
</p>
<p>
  <%= form.submit %>
</p>
<% end %>

答案 1 :(得分:1)

这应该使用嵌套路线而不是通过机会来完成 表单中的ID。

# config/routes.rb
Rails.application.routes.draw do

  # create the route: GET /opportunities/:id
  # helper: opportunity_path(opportunity || opportunity_id)
  # handled by: OpportunitiesController#show
  resources :opportunities, only: :show do

    # create the route: POST /opportunities/:opportunity_id/opportunity_enrolments
    # helper: opportunity_opportunity_enrolments_path(opportunity || opportunity_id)
    # handled by: OpportunityEnrolmentsController#create
    resources :opportunity_enrolments, only: :create
  end

end

# app/controllers/opportunity_enrolments_controller.rb
class OpportunityEnrolmentsController < ApplicationController
  # opportunity should be set for every nested action, create in this scenario
  before_action :set_opportunity, only: :create

  def create
    # initialize a new opportunity enrolment with opportunity id set to
    # the id of the current opportunity
    @opportunity_enrolment = @opportunity.opportunity_enrolments.build
    # set the user id equal to the current user
    @opportunity_enrolment.user = current_user

    # assign the passed attributes by the form and try to save the record
    # if your form doesn't contain any attributes, call #save instead
    if @opportunity_enrolment.update(opportunity_enrolment_params)
      redirect_to @opportunity
    else
      # display errors using @opportunity_enrolment.errors in the form, see note
      render 'opportunities/show' 
    end
  end

  private

  def opportunity_enrolment_params
    # if you only need to set the attributes opportunity_id and user_id
    # you can leave this call out and call #save instead of #update
    # ...
  end

  def set_opportunity
    # params[:opportunity_id] is retrieved from the current path, it is not
    # a query or request body param
    @opportunity = Opportunity.find(params[:opportunity_id])
  end
end

<% # app/views/opportunities/show.html.erb %>

<% # If rendered from opportunity show: opportunity enrolment is not set thus a new     %>
<% # opportunity enrolment will be initialized. If rendered from the opportunity        %>
<% # enrolment create action: opportunity enrolment will already be present with errors %>
<% # set, no new opportunity will be initialized.                                       %>
<% @opportunity_enrolment ||= @opportunity.opportunity_enrolments.build %>

<% # Passing an array containing an opportunity and an opportunity enrolment will build  %>
<% # the path in 3 steps. 1) Is opportunity a new record? Use /opportunities, if not use %>
<% # /opportunities/:id. 2) Is opportunity enrolment a new record? Use                   %>
<% # /opportunity_enrolments, if not use /opportunity_enrolments/:id. 3) Is the last     %>
<% # element in the array a new record? Use POST, if not use PUT.                        %>
<% # Combining the above together you get the path:                                      %>
<% # POST /opportunities/:opportunity_id/opportunity_enrolments                          %>
<% # Handled by OpportunityEnrolmentsController#create (see routes).                     %>
<%= form_with model: [@opportunity, @opportunity_enrolment], local: true do |form| %>
  <p><strong>Name:</strong><%= @opportunity.voppname %></p>
  <p><strong>Shortdescr:</strong><%= @opportunity.shortdescr %></p>
  <p><%= form.submit %></p>
<% end %>

注意:如果您要阅读nested resources,请不要跳过 有关浅层嵌套的部分。它使您的路由和应用程序更清洁。 可以访问错误页面here