代码之家  ›  专栏  ›  技术社区  ›  DollarChills

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

  •  0
  • DollarChills  · 技术社区  · 8 年前

    我有一个试验模型,它在创建时创建治疗选择和重复。

    通过重复,我希望通过一个:通过关联来保存试验和治疗选择。

    提交试用表时,我收到以下错误:

    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
    

    治疗选择模型有一个助手,如下所示,用户可以动态添加。 三助手

    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 回复  |  直到 8 年前
        1
  •  1
  •   max Mike Williams    8 年前

    你的安排不太对。

    为了加入重复试验,你需要在治疗选择中建立一个关联来完成:

    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不了解嵌套属性是否通过另一个关联。

    您还应该将调用嵌套到字段\u中。

    <%= 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
  •  2
  •   neume    8 年前

    您的代码 create 行动 @trial.treatment_selections.build 将返回 TreatmentSelection 没有定义的对象 repetitions 在其模型中定义。

    也许你的意思是:

    @trial.treatment_selections.build
    @trial.repetitions.build
    
        3
  •  0
  •   Mr. Perfect    8 年前

    您正在处理选择模型上构建重复对象,该模型不指定处理选择和重复模型之间的关系, 你需要在治疗选择模型中添加has-many:repetitions以使其起作用。