如何在设置中避免ActiveRecord :: HasManyThroughCantAssociateThroughOasOneOrManyReflection错误?

时间:2015-11-25 11:29:56

标签: ruby-on-rails

我的rails应用程序中有以下模型:

class SaleContact < ActiveRecord::Base
 validates :key_contact_id, presence: true, uniqueness: { scope: :sales_opportunity_id, message: "Contact already added!" }
 validates :sales_opportunity_id, presence: true
 belongs_to :key_contact, inverse_of: :sale_contacts
 belongs_to :sales_opportunity, inverse_of: :sale_contacts
 has_many :phone_numbers, :through => :key_contact
 accepts_nested_attributes_for :phone_numbers
end

我正在尝试通过选择key_contact(假设key_contact已存在)从sales_opportunity屏幕创建sale_contact。我还希望能够使用fields_for和嵌套属性同时添加phone_number。

class KeyContact < ActiveRecord::Base
 validates :first_name, :last_name, :company_id, presence: true
 has_many :phone_numbers, dependent: :destroy
 belongs_to :company
 has_many :sales_opportunities, :through => :sale_contacts
 has_many :sale_contacts, dependent: :destroy
end

假设我已经创建了key_contact并将其分配给拥有它的公司。

class PhoneNumber < ActiveRecord::Base
 validates :number, :key_contact_id, presence: true
 belongs_to :key_contact
end

这里没什么神奇的 - 只是一个非常基本的电话号码模型。

在sales_opportunity页面上,我可以加载一个添加新sale_contact的模式。它是由AJAX加载的引导模式,但我不认为这很重要(为了简洁,我只包括表单部分):

<%= form_for(@sale_contact, :html => {role: :form, 'data-model' => 'sale_contact'}, remote: true) do |f| %>
      <% if @sale_contact.errors.any? %>
        <div id="error_explanation">
          <h2><%= pluralize(@sale_contact.errors.count, "error") %> prohibited this sale_contact from being saved:</h2>

          <ul>
          <% @sale_contact.errors.full_messages.each do |message| %>
            <li><%= message %></li>
          <% end %>
          </ul>
        </div>
      <% end %>

      <div class="form-group" id= "contact_error">
        <%= f.label :key_contact_id, :class => "control-label" %>
        <div id="contact_select">
        <%= f.collection_select :key_contact_id, @sales_opportunity.company.key_contacts(:full_name), :id, :full_name %>
        </div>
        <span class="warning-block"></span>
        <span class="help-block"></span>
      </div>

        <div class="form-group">
          <%= f.label :role, :class => "control-label" %>
          </br>
            <%= f.select(:role, options_for_select(@roles.collect { |r| [r[0].humanize, r[0]] }, selected: @sale_contact.role), {}) %>
          <span class="help-block"></span>
        </div>

        <div class="form-group">
          <%= f.label :preference, :class => "control-label" %>
          </br>
            <%= f.select(:preference, options_for_select(@preferences.collect { |r| [r[0].humanize, r[0]] }, selected: @sale_contact.preference), {}) %>
          <span class="help-block"></span>
        </div>

        <%= f.fields_for(:phone_numbers) do |phone| %>
            <div class="form-group">
              <%= phone.label :number, "Phone Number", :class => "control-label" %>
            </br>
              <%= phone.text_field :number, :placeholder => "Enter phone number (optional)" %>
              <span class="help-block"></span>
            </div>
            <div>
              <%= phone.hidden_field :key_contact_id %>
            </div>
        <% end %>

        <div class="form-group">
          <%= f.hidden_field :sales_opportunity_id, :value => @sales_opportunity.id %>
        </div>
          <%= f.submit "Save", class: "btn btn-large btn-success", data: { disable_with: "Submitting..." }%>
    <% end %>

从我的sale_contact_controller新动作:

  def new
   @sale_contact = SaleContact.new
   @sale_contact.phone_numbers.build
   @sales_opportunity = SalesOpportunity.find(params[:sales_opportunity_id])
   @company = @sales_opportunity.company
   @roles = SaleContact.roles
   @preferences = SaleContact.preferences
   render :modal_form
  end

   def sale_contact_params
     params.require(:sale_contact).permit(:key_contact_id, :sales_opportunity_id, :role, :preference, phone_numbers_attributes: [:number, :id])
    end

当通过AJAX检索模态或key_contact选择下拉列表更改以将key_contact_id引入phone_numbers_attributes时,我运行javascript片段;如果我不这样做,我得到422不可处理的实体错误,因为我没有通过我的key_contact_id发送。

使用此设置,模式弹出视图并具有正确的字段(包括sale_contact和phone_number),但不会保存这些模型 - 我收到500错误:

Completed 500 Internal Server Error in 17ms

ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection (Cannot modify association 'SaleContact#phone_numbers' because the source reflection class 'PhoneNumber' is associated to 'KeyContact' via :has_many.):
  app/controllers/sale_contacts_controller.rb:50:in `block in create'
  app/controllers/sale_contacts_controller.rb:49:in `create'

我尝试过其他方法,例如在sale_contact create方法中设置key_contact_id,但我得到了相同的结果。当我没有传递key_contact_id(422错误)并且在我这样做时抱怨(由于rails关联在内部设置key_contact_id而导致500内部服务器错误)时,我无法理解为什么这个设置会抱怨。

Rails希望我在这做什么?

1 个答案:

答案 0 :(得分:1)

我的表单存在类似问题,但我可以使用嵌套的field_for标记传递它

<强> Proposal.rb

has_many :proposal_section_reviews, :through => :proposal_review_status
has_one :proposal_review_status, :dependent => :destroy
attr_accessible :proposal_review_status_attributes, :proposal_review_status_attributes, :proposal_section_reviews_attributes
accepts_nested_attributes_for :proposal_review_status, :proposal_section_reviews

<强> ProposalSectionReview.rb

belongs_to :proposal_review_status

<强> ProposalReviewStatus.rb

has_many :proposal_section_reviews, :dependent => :destroy

<强> _form.html.haml

= form_for proposal do |f|
  = f.fields_for :proposal_review_status do |ff|
    = ff.fields_for :proposal_section_reviews

所以在你的实例中我会尝试

<%= f.fields_for(:key_contact) do |key| %>
    <%= key.fields_for(:phone_numbers) do |phone| %>
        <div class="form-group">
          <%= phone.label :number, "Phone Number", :class => "control-label" %>
        </br>
          <%= phone.text_field :number, :placeholder => "Enter phone number (optional)" %>
          <span class="help-block"></span>
        </div>
        <div>
          <%= phone.hidden_field :key_contact_id %>
        </div>
    <% end %>
<% end %>

希望这有帮助