具有嵌套属性的未定义方法

时间:2018-10-22 04:55:40

标签: ruby-on-rails ruby-on-rails-5

我有一个Trial模型,该模型在创建时创建treatment_selection和重复。

我想通过重复来保存我试图通过:through关联进行的trial_id和treatment_selection_id。

提交试用表格时,出现以下错误:

undefined method `repetitions' for #<TreatmentSelection:0x007ff76b8c3830>

试用版控制器

def create
    @trial = Trial.new(trial_params)
    @trial.treatment_selections.build.repetitions.build
end

private
    def trial_params
        params.require(:trial).permit(:year, :cultivation_id, :region_id, :cooperator_id, :field_id, :property_id, treatment_selections_attributes: [TreatmentSelection.attribute_names.map(&:to_sym).push(:_destroy), repetitions_attributes: [:trial_id, :treatment_selection_id]])
  end

模型

class Trial < ApplicationRecord
  has_many :treatment_selections, dependent: :destroy

  has_many :repetitions, through: :treatment_selections, source: :treatment

  accepts_nested_attributes_for :repetitions, allow_destroy: true
  accepts_nested_attributes_for :treatment_selections, allow_destroy: true
end

class TreatmentSelection < ApplicationRecord
  belongs_to :trial
  belongs_to :treatment
end

class Repetition < ApplicationRecord
  belongs_to :treatment_selection
end

treatment_selection模型具有以下帮助器,用户可以动态添加该帮助器。 trials_helper

module TrialsHelper
  def link_to_add_row(name, f, association, **args)
    new_object = f.object.send(association).klass.new
    id = new_object.object_id
    fields = f.fields_for(association, new_object, child_index: id) do |builder|
      render(association.to_s.singularize, f: builder)
    end
    link_to(name, '#', class: "add_fields " + args[:class], data: {id: id, fields: fields.gsub("\n", "")})
  end
end

架构

 create_table "repetitions", force: :cascade do |t|
    t.integer "trial_id"
    t.integer "treatment_selection_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

登录

Started POST "/trials" for 127.0.0.1 at 2018-10-22 14:45:57 +1000
Processing by TrialsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"I2QheHAPOuhnv61Ghs0UoVgXcbYvX73AA7+nI/JTvRU4lTRYJbOdCstuTt/i7/4H2dV6wv4Vdt6zcQ7oo1id6w==", "trial"=>{"year(2i)"=>"1", "year(3i)"=>"1", "year(1i)"=>"2018", "region_id"=>"3", "cooperator_id"=>"1", "property_id"=>"1", "field_id"=>"5", "cultivation_id"=>"1", "treatment_selections_attributes"=>{"0"=>{"treatment_id"=>"3", "ambassador"=>"0", "_destroy"=>"false"}, "1"=>{"treatment_id"=>"", "ambassador"=>"0", "_destroy"=>"false"}, "2"=>{"treatment_id"=>"", "ambassador"=>"0", "_destroy"=>"false"}}}, "commit"=>"Submit"}
Completed 500 Internal Server Error in 65ms (ActiveRecord: 11.6ms)


NoMethodError (undefined method `repetitions' for #<TreatmentSelection:0x007ff76b8c3830>):

app/controllers/trials_controller.rb:48:in `block in create'
app/controllers/trials_controller.rb:47:in `times'
app/controllers/trials_controller.rb:47:in `create'

试用表格

<%= form_with(model: trial, local: true) do |f| %>
    <%= f.date_select :year, {order: [:year] %>
    ... Other Trial fields...
    <%= f.fields_for :treatment_selections do |builder| %>
        <%= render 'treatment_selection', f: builder %>
    <% end %>

    <%= f.submit 'Submit' %>
<% end %>

部分治疗选择

<%= f.collection_select :treatment_id, Treatment.order('name'), :id, :name %>
  <%= f.check_box :ambassador %>
  <%= f.hidden_field :_destroy, as: :hidden %>
  <%= link_to 'Delete', '#', class: "remove_treatment" %>

3 个答案:

答案 0 :(得分:2)

您在create操作@trial.treatment_selections.build中的代码将返回一个TreatmentSelection对象,该对象没有模型中定义的repetitions的定义。

也许你是说:

@trial.treatment_selections.build
@trial.repetitions.build

答案 1 :(得分:1)

您的关联不太正确。

要加入Trial to Repetition,您需要在TreatmentSelection中设置关联以进行以下操作:

class TreatmentSelection < ApplicationRecord
  belongs_to :trial
  belongs_to :treatment
  has_many :repetitions, dependent: :destroy
end

这可以让您正确地进行此关联:

class Trial < ApplicationRecord
  has_many :treatment_selections, dependent: :destroy
  has_many :repetitions, through: :treatment_selections
end

您还错误地使用了source选项。当必须告诉ActiveRecord联接模型上哪个关联与所需模型相对应时(名称不匹配时),将使用它。

您还应该删除repetions.trial_id列,因为它会造成不必要的重复。

您还需要将关联添加到上链的重复项中:

class Repetition < ApplicationRecord
  belongs_to :treatment_selection
  has_one :treatment, through: :treatment_selection
  has_one :trial, through: :treatment_selection
end

您还应该按照以下步骤设置嵌套属性:

class Trial < ApplicationRecord
  # ...
  accepts_nested_attributes_for :treatment_selections, allow_destroy: true
end 

class TreatmentSelection < ApplicationRecord
  # ...
  accepts_nested_attributes_for :repetitions, allow_destroy: true
end

这很关键,因为属性应该嵌套两个级别。

{ 
   trial: {
     treatment_selection_attributes: [
        {
          treatment_id: 1,   
          repetition_attributes: [
            { foo: 'bar' }
          ]
        },
        {
          treatment_id: 1,   
          repetition_attributes: [
            { foo: 'bar' }
          ]
        }
     ]
   }
}

ActiveRecord无法理解嵌套属性是否经过另一个关联。

您还应该嵌套对fields_for的调用。

<%= form_with(model: trial, local: true) do |f| %>
  <%= f.fields_for :treatment_selections do |tsf| %>
    <%= tsf.fields_for :repetitions do |rf| %>
    <% end %>
  <% end %>
<% end %>

答案 2 :(得分:0)

您要在TreatmentSelection模型上构建重复对象,该对象未指定TreatmentSelection和Repetition模型之间的关系, 您需要在TreatmentSelection模型中添加has_many:repetitions才能使其正常工作。