我有两个类,用户和机会,它们利用具有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),以便可以将一行添加到机会表中?
谢谢
答案 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。