Rails - 在 has_many 到 table 上插入

Rails - Insert on a has_many through table

我有两个 class 用户和机会,它们利用 table 和 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_enrolments table 添加新行。

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 方法,所以我在提交时收到 "Undefined method `id' for nil:NilClass" 错误。我尝试了一种不同的方式,在表单中有一个隐藏字段(理解它不安全)并且对象仍然没有通过,但我通过了未定义方法错误。

如何将对象信息(Opportunity)传递给另一个 class(OpportunityEnrolments)以便我可以向 opportunity_enrolments table 添加一行?

谢谢

如果您使用隐藏字段,则需要在 opportunity_enrolments_controller 的创建操作中实例化商机。

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

这应该使用嵌套路由来完成,而不是传递机会 表格中的 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.