Rails-如何在不使用accepts_nested_attributes_for的情况下管理嵌套属性? [英] Rails - How to manage nested attributes without using accepts_nested_attributes_for?

本文介绍了Rails-如何在不使用accepts_nested_attributes_for的情况下管理嵌套属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是我遇到了accepts_nested_attributes_for的局限性,因此我需要弄清楚如何自行复制该功能以获得更大的灵活性. (请参阅下面的内容,这实际上使我感到困惑.)我的问题是:如果我想模仿和增加accepts_nested_attributes_for,我的表单,控制器和模型应该是什么样?真正的诀窍是我需要能够使用现有的关联/属性来更新现有模型和新模型.

我正在构建一个使用嵌套表单的应用程序.我最初将此RailsCast用作蓝图(利用accepts_nested_attributes_for): Railscast 196:嵌套模型表格.

我的应用程序是包含作业(任务)的清单,我让用户更新清单(名称,描述)并以一种形式添加/删除关联的作业.效果很好,但是当我将其合并到应用程序的另一个方面时,就会遇到问题:通过版本控制的历史记录.

我的应用程序很大一部分是我需要记录有关模型和关联的历史信息.我最终滚动了自己的版本(

代码-checklists_controller.rb#Update

中的代码段

def update
  @oldChecklist = Checklist.find(params[:id])

# Do some checks to determine if we need to do the new copy/archive stuff
  @newChecklist = @oldChecklist.dup
  @newChecklist.parent_id = (@oldChecklist.parent_id == 0) ? @oldChecklist.id : @oldChecklist.parent_id
  @newChecklist.predecessor_id = @oldChecklist.id
  @newChecklist.version = (@oldChecklist.version + 1)
  @newChecklist.save

# Now I've got a new checklist that looks like the old one (with some updated versioning info).

# For the jobs associated with the old checklist, do some similar archiving and creating new versions IN THE JOIN TABLE
  @oldChecklist.checklists_jobs.archived_state(:false).each do |u|
    x = u.dup
    x.checklist_id = @newChecklist.id
    x.save
    u.archive
    u.save
  end

# Now the new checklist's join table entries look like the old checklist's entries did
# BEFORE the form was submitted; but I want to update the NEW Checklist so it reflects 
# the updates made in the form that was submitted.
# Part of the params[:checklist] has is "jobs_attributes", which is handled by
# accepts_nested_attributes_for. The problem is I can't really manipulate that hash very
# well, and I can't do a direct update with those attributes on my NEW model (as I'm 
# trying in the next line) due to a built-in limitation.
  @newChecklist.update_attributes(params[:checklist])

这就是我遇到accepts_nested_attributes_for限制的地方(很好的记录在这里 .我得到了无法为ID = Y的Model2找到ID = X的Model1"的异常,这基本上是设计好的.

那么,我如何创建多个嵌套模型并以类似于accepts_nested_attributes_for的父模型的形式添加/删除它们,但要靠我自己呢?

我所见过的选择-是最好的选择之一吗? 真正的诀窍是我需要能够使用现有的关联/属性来更新现有模型和新模型.我无法链接它们,因此只给它们命名.

Redtape(在github上) Virtus(也是github)

感谢您的帮助!

解决方案

由于Mario对我的问题发表了评论,并问我是否解决了这个问题,所以我想我会分享我的解决方案.

我应该说,我确定这不是一个很好的解决方案,也不是很好的代码.但这是我想出的,而且有效.由于此问题是非常技术性的,因此我不在此处发布伪代码-我将发布Checklist模型和Checklists控制器更新操作的完整代码(无论如何,适用于此问题的代码部分).我也很确定我的交易区块实际上没有做任何事情(我需要修复这些问题).

基本思想是我手动执行更新操作.我不依靠update_attributes(和accepts_nested_attributes_for),而是分两个阶段手动更新清单:

  1. 实际的清单对象是否已更改(清单仅具有名称和描述)?如果是这样,请创建一个新的清单,将新清单设为旧清单,然后为新清单设置添加或选择的所有作业.
  2. 如果清单本身未更改(名称和描述保持不变),分配给它的作业是否发生了变化?如果有,则存档已删除的作业分配,并添加所有新的作业分配.

我认为这里有些提交"的东西可以忽略不计(确定核对清单的更改是否重要甚至是重要的逻辑-如果没有任何提交(核对清单的历史数据的记录)然后进行更新清单,而无需执行任何此类归档或添加/减去作业的工作.)

我不知道这是否会有所帮助,但这还是可以的.

代码-checklist.rb(模型)

class Checklist < ActiveRecord::Base
  scope :archived_state, lambda {|s| where(:archived => s) }

  belongs_to :creator, :class_name => "User", :foreign_key => "creator_id"
  has_many :submissions
  has_many :checklists_jobs, :dependent => :destroy, :order => 'checklists_jobs.job_position'#, :conditions => {'archived_at' => nil}
  has_many :jobs, :through => :checklists_jobs
  has_many :unarchived_jobs, :through => :checklists_jobs, 
           :source => :job, 
           :conditions => ['checklists_jobs.archived = ?', false], :order => 'checklists_jobs.job_position'
  has_many :checklists_workdays, :dependent => :destroy
  has_many :workdays, :through => :checklists_workdays

  def make_child_of(old_checklist)
    self.parent_id = (old_checklist.parent_id == 0) ? old_checklist.id : old_checklist.parent_id
    self.predecessor_id = old_checklist.id
    self.version = (old_checklist.version + 1)
  end

  def set_new_jobs(new_jobs)
    new_jobs.to_a.each do |job|
      self.unarchived_jobs << Job.find(job) unless job.nil?
    end
  end

  def set_jobs_attributes(jobs_attributes, old_checklist)
    jobs_attributes.each do |key, entry| 
      # Job already exists and should have a CJ
      if entry[:id] && !(entry[:_destroy] == '1')
       old_cj = old_checklist.checklists_jobs.archived_state(:false).find_by_job_id(entry[:id])
       new_cj = ChecklistsJob.new job_position: old_cj.job_position, job_required: old_cj.job_required
       new_cj.checklist = self
       new_cj.job = old_cj.job
       new_cj.save!
      # New job, should be created and added to new checklist only
      else
       unless entry[:_destroy] == '1'
         entry.delete :_destroy
         self.jobs << Job.new(entry)
       end
      end
    end
  end

  def set_checklists_workdays!(old_checklist)
    old_checklist.checklists_workdays.archived_state(:false).each do |old_cw|
      new_cw = ChecklistsWorkday.new checklist_position: old_cw.checklist_position
      new_cw.checklist = self
      new_cw.workday = old_cw.workday
      new_cw.save!
      old_cw.archive
      old_cw.save!
    end
  end

  def update_checklists_jobs!(jobs_attributes)
    jobs_attributes.each do |key, entry|
      if entry[:id] # Job was on self when #edit was called
        old_cj = self.checklists_jobs.archived_state(:false).find_by_job_id(entry[:id])
        #puts "OLD!! "+old_cj.id.to_s
        unless entry[:_destroy] == '1' 
          new_cj = ChecklistsJob.new job_position: old_cj.job_position, job_required: old_cj.job_required
          new_cj.checklist = self
          new_cj.job = old_cj.job
          new_cj.save!
        end
        old_cj.archive
        old_cj.save!
      else # Job was created on this checklist
        unless entry[:_destroy] == '1'
          entry.delete :_destroy
          self.jobs << Job.new(entry)
        end
      end
    end
  end
end

代码-checklists_controller.rb(控制器)

class ChecklistsController < ApplicationController
  before_filter :admin_user

  def update
    @checklist = Checklist.find(params[:id])
    @testChecklist = Checklist.find(params[:id])
    @oldChecklist = Checklist.find(params[:id])
    @job_list = @checklist.unarchived_jobs.exists? ? Job.archived_state(:false).where( 'id not in (?)', @checklist.unarchived_jobs) : Job.archived_state(:false)

    checklist_ok = false
    # If the job is on a submission, do archiving/copying; else just update it
    if @checklist.submissions.count > 0
      puts "HERE A"
      # This block will tell me if I need to make new copies or not
      @testChecklist.attributes=(params[:checklist])
      jobs_attributes = params[:checklist][:jobs_attributes]
      if @testChecklist.changed?
        puts "HERE 1"
        params[:checklist].delete :jobs_attributes        
        @newChecklist = Checklist.new(params[:checklist])
        @newChecklist.creator = current_user
        @newChecklist.make_child_of(@oldChecklist)
        @newChecklist.set_new_jobs(params[:new_jobs])

        begin
          ActiveRecord::Base.transaction do
            @newChecklist.set_jobs_attributes(jobs_attributes, @oldChecklist) if jobs_attributes
            @newChecklist.set_checklists_workdays!(@oldChecklist)
            @newChecklist.save!
            @oldChecklist.archive
            @oldChecklist.save!
            @checklist = @newChecklist
            checklist_ok = true
          end
          rescue ActiveRecord::RecordInvalid 
          # This is a NEW checklist, so it's acting like it's "new" - WRONG?
          puts "RESCUE 1"
          @checklist = @newChecklist
          @jobs = @newChecklist.jobs     
          checklist_ok = false
        end              
      elsif @testChecklist.changed_for_autosave? || params.has_key?(:new_jobs)
        puts "HERE 2"    
        # Associated Jobs have changed, so archive old checklists_jobs,
        # then set checklists_jobs based on params[:checklist][:jobs_attributes] and [:new_jobs]

        @checklist.set_new_jobs(params[:new_jobs])

        begin
          ActiveRecord::Base.transaction do
            @checklist.update_checklists_jobs!(jobs_attributes) if jobs_attributes
            @checklist.save!
            checklist_ok = true
          end
          rescue ActiveRecord::RecordInvalid      
          puts "RESCUE 2"
          @jobs = @checklist.unarchived_jobs
          checklist_ok = false
        end
      else
        checklist_ok = true # There were no changes to the Checklist or Jobs
      end
    else
      puts "HERE B"
      @checklist.set_new_jobs(params[:new_jobs])
      begin
        ActiveRecord::Base.transaction do
          @checklist.update_attributes(params[:checklist])
          checklist_ok = true
        end
        rescue ActiveRecord::RecordInvalid 
        puts "RESCUE B"
        @jobs = @checklist.jobs     
        checklist_ok = false
      end
    end

    respond_to do |format|
      if  checklist_ok
        format.html { redirect_to @checklist, notice: 'List successfully updated.' }
        format.json { head :no_content }
      else
        flash.now[:error] = 'There was a problem updating the List.'
        format.html { render action: "edit" }
        format.json { render json: @checklist.errors, status: :unprocessable_entity }
      end
    end
  end
end

代码-清单表单

<%= form_for @checklist, :html => { :class => 'form-inline' } do |f| %>
  <div>
    <%= f.text_area :name, :rows => 1, :placeholder => 'Name the list...', :class => 'autoresizer checklist-name' %></br>
    <%= f.text_area :description, :rows => 1, :placeholder => 'Optional description...', :class => 'autoresizer' %>
  </div>

  <%= f.fields_for :jobs, :html => { :class => 'form-inline' } do |j| %>
    <%= render "job_fields", :j => j  %>
  <% end %>

  <span class="add-new-job-link"><%= link_to_add_fields "add a new job", f, :jobs %></span>
  <div class="form-actions">
    <%= f.submit nil, :class => 'btn btn-primary' %>
    <%= link_to 'Cancel', checklists_path, :class => 'btn' %>
  </div>

  <% unless @job_list.empty? %>
    <legend>Add jobs from the Job Bank</legend>

    <% @job_list.each do |job| %>
      <div class="toggle">
        <label class="checkbox text-justify" for="<%=dom_id(job)%>">
          <%= check_box_tag "new_jobs[]", job.id, false, id: dom_id(job) %><strong><%= job.name %></strong> <small><%= job.description %></small>
        </label>
      </div>
    <% end %>

    <div class="form-actions">
      <%= f.submit nil, :class => 'btn btn-primary' %>
      <%= link_to 'Cancel', checklists_path, :class => 'btn' %>
    </div>
  <% end %>
<% end %>

My problem is I've run into limitations of accepts_nested_attributes_for, so I need to figure out how to replicate that functionality on my own in order to have more flexibility. (See below for exactly what's hanging me up.) So my question is: What should my form, controller and models look like if I want to mimmic and augment accepts_nested_attributes_for? The real trick is I need to be able to update both existing AND new models with existing associations/attributes.

I'm building an app that uses nested forms. I initially used this RailsCast as a blueprint (leveraging accepts_nested_attributes_for): Railscast 196: Nested Model Form.

My app is checklists with jobs (tasks), and I'm letting the user update the checklist (name, description) and add/remove associated jobs in a single form. This works well, but I run into problems when I incorporate this into another aspect of my app: history via versioning.

A big part of my app is that I need to record historical information for my models and associations. I ended up rolling my own versioning (here is my question where I describe my decision process/considerations), and a big part of that is a workflow where I need to create a new version of an old thing, make updates to the new version, archive the old version. This is invisible to the user, who sees the experience as simply updating a model through the UI.

Code - models

#checklist.rb
class Checklist < ActiveRecord::Base
  has_many :jobs, :through => :checklists_jobs
  accepts_nested_attributes_for :jobs, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end

#job.rb
class Job < ActiveRecord::Base
  has_many :checklists, :through => :checklists_jobs
end

Code - current form (NOTE: @jobs is defined as unarchived jobs for this checklist in the checklists controller edit action; so is @checklist)

<%= simple_form_for @checklist, :html => { :class => 'form-inline' } do |f| %>
  <fieldset>
    <legend><%= controller.action_name.capitalize %> Checklist</legend><br>

    <%= f.input :name, :input_html => { :rows => 1 }, :placeholder => 'Name the Checklist...', :class => 'autoresizer'  %>
    <%= f.input :description, :input_html => { :rows => 3 }, :placeholder => 'Optional description...', :class => 'autoresizer' %>

    <legend>Jobs on this Checklist - [Name] [Description]</legend>

    <%= f.fields_for :jobs, @jobs, :html => { :class => 'form-inline' } do |j| %>
        <%= render "job_fields_disabled", :j => j %>
    <% end %>
    </br>
    <p><%= link_to_add_fields "+", f, :jobs %></p>

    <div class="form-actions">
      <%= f.submit nil, :class => 'btn btn-primary' %>
      <%= link_to 'Cancel', checklists_path, :class => 'btn' %>
    </div>
  </fieldset>
<% end %>

Code - snippet from checklists_controller.rb#Update

def update
  @oldChecklist = Checklist.find(params[:id])

# Do some checks to determine if we need to do the new copy/archive stuff
  @newChecklist = @oldChecklist.dup
  @newChecklist.parent_id = (@oldChecklist.parent_id == 0) ? @oldChecklist.id : @oldChecklist.parent_id
  @newChecklist.predecessor_id = @oldChecklist.id
  @newChecklist.version = (@oldChecklist.version + 1)
  @newChecklist.save

# Now I've got a new checklist that looks like the old one (with some updated versioning info).

# For the jobs associated with the old checklist, do some similar archiving and creating new versions IN THE JOIN TABLE
  @oldChecklist.checklists_jobs.archived_state(:false).each do |u|
    x = u.dup
    x.checklist_id = @newChecklist.id
    x.save
    u.archive
    u.save
  end

# Now the new checklist's join table entries look like the old checklist's entries did
# BEFORE the form was submitted; but I want to update the NEW Checklist so it reflects 
# the updates made in the form that was submitted.
# Part of the params[:checklist] has is "jobs_attributes", which is handled by
# accepts_nested_attributes_for. The problem is I can't really manipulate that hash very
# well, and I can't do a direct update with those attributes on my NEW model (as I'm 
# trying in the next line) due to a built-in limitation.
  @newChecklist.update_attributes(params[:checklist])

And that's where I run into the accepts_nested_attributes_for limitation (it's documented pretty well here. I get the "Couldn't find Model1 with ID=X for Model2 with ID=Y" exception, which is basically as-designed.

So, how can I create multiple nested models and add/remove them on the parent model's form similar to what accepts_nested_attributes_for does, but on my own?

The options I've seen - is one of these best? The real trick is I need to be able to update both existing AND new models with existing associations/attributes. I can't link them, so I'll just name them.

Redtape (on github) Virtus (also github)

Thanks for your help!

解决方案

Since Mario commented on my question and asked if I solved it, I thought I would share my solution.

I should say that I'm sure this isn't a very elegant solution, and it's not great code. But it's what I came up with, and it works. Since this question is pretty technical, I'm not posting pseudo-code here - I'm posting the full code for both the Checklist model and the Checklists controller update action (the parts of the code that apply to this question, anyway). I'm also pretty sure my transaction blocks aren't actually doing anything (I need to fix those).

The basic idea is I broke out the update action manually. Rather than relying on update_attributes (and accepts_nested_attributes_for), I manually update the checklist in two phases:

  1. Did the actual checklist object change (a checklist only has a name and description)? If it did, create a new checklist, make the new one a child of the old one, and set the new one up with whatever jobs were added or selected for it.
  2. If the checklist itself didn't change (name and description stayed the same), did the jobs assigned to it change? If they did, archive job assignments that were removed, and add any new job assignments.

There's some "submission" stuff that I think is safe to ignore here (it's basically logic to determine if it even matters how the checklist changed - if there aren't any submissions (records of a checklist's historical data) then just update the checklist in place without doing any of this archiving or adding/subtracting jobs stuff).

I don't know if this will be helpful, but here it is anyway.

Code - checklist.rb (model)

class Checklist < ActiveRecord::Base
  scope :archived_state, lambda {|s| where(:archived => s) }

  belongs_to :creator, :class_name => "User", :foreign_key => "creator_id"
  has_many :submissions
  has_many :checklists_jobs, :dependent => :destroy, :order => 'checklists_jobs.job_position'#, :conditions => {'archived_at' => nil}
  has_many :jobs, :through => :checklists_jobs
  has_many :unarchived_jobs, :through => :checklists_jobs, 
           :source => :job, 
           :conditions => ['checklists_jobs.archived = ?', false], :order => 'checklists_jobs.job_position'
  has_many :checklists_workdays, :dependent => :destroy
  has_many :workdays, :through => :checklists_workdays

  def make_child_of(old_checklist)
    self.parent_id = (old_checklist.parent_id == 0) ? old_checklist.id : old_checklist.parent_id
    self.predecessor_id = old_checklist.id
    self.version = (old_checklist.version + 1)
  end

  def set_new_jobs(new_jobs)
    new_jobs.to_a.each do |job|
      self.unarchived_jobs << Job.find(job) unless job.nil?
    end
  end

  def set_jobs_attributes(jobs_attributes, old_checklist)
    jobs_attributes.each do |key, entry| 
      # Job already exists and should have a CJ
      if entry[:id] && !(entry[:_destroy] == '1')
       old_cj = old_checklist.checklists_jobs.archived_state(:false).find_by_job_id(entry[:id])
       new_cj = ChecklistsJob.new job_position: old_cj.job_position, job_required: old_cj.job_required
       new_cj.checklist = self
       new_cj.job = old_cj.job
       new_cj.save!
      # New job, should be created and added to new checklist only
      else
       unless entry[:_destroy] == '1'
         entry.delete :_destroy
         self.jobs << Job.new(entry)
       end
      end
    end
  end

  def set_checklists_workdays!(old_checklist)
    old_checklist.checklists_workdays.archived_state(:false).each do |old_cw|
      new_cw = ChecklistsWorkday.new checklist_position: old_cw.checklist_position
      new_cw.checklist = self
      new_cw.workday = old_cw.workday
      new_cw.save!
      old_cw.archive
      old_cw.save!
    end
  end

  def update_checklists_jobs!(jobs_attributes)
    jobs_attributes.each do |key, entry|
      if entry[:id] # Job was on self when #edit was called
        old_cj = self.checklists_jobs.archived_state(:false).find_by_job_id(entry[:id])
        #puts "OLD!! "+old_cj.id.to_s
        unless entry[:_destroy] == '1' 
          new_cj = ChecklistsJob.new job_position: old_cj.job_position, job_required: old_cj.job_required
          new_cj.checklist = self
          new_cj.job = old_cj.job
          new_cj.save!
        end
        old_cj.archive
        old_cj.save!
      else # Job was created on this checklist
        unless entry[:_destroy] == '1'
          entry.delete :_destroy
          self.jobs << Job.new(entry)
        end
      end
    end
  end
end

Code - checklists_controller.rb (controller)

class ChecklistsController < ApplicationController
  before_filter :admin_user

  def update
    @checklist = Checklist.find(params[:id])
    @testChecklist = Checklist.find(params[:id])
    @oldChecklist = Checklist.find(params[:id])
    @job_list = @checklist.unarchived_jobs.exists? ? Job.archived_state(:false).where( 'id not in (?)', @checklist.unarchived_jobs) : Job.archived_state(:false)

    checklist_ok = false
    # If the job is on a submission, do archiving/copying; else just update it
    if @checklist.submissions.count > 0
      puts "HERE A"
      # This block will tell me if I need to make new copies or not
      @testChecklist.attributes=(params[:checklist])
      jobs_attributes = params[:checklist][:jobs_attributes]
      if @testChecklist.changed?
        puts "HERE 1"
        params[:checklist].delete :jobs_attributes        
        @newChecklist = Checklist.new(params[:checklist])
        @newChecklist.creator = current_user
        @newChecklist.make_child_of(@oldChecklist)
        @newChecklist.set_new_jobs(params[:new_jobs])

        begin
          ActiveRecord::Base.transaction do
            @newChecklist.set_jobs_attributes(jobs_attributes, @oldChecklist) if jobs_attributes
            @newChecklist.set_checklists_workdays!(@oldChecklist)
            @newChecklist.save!
            @oldChecklist.archive
            @oldChecklist.save!
            @checklist = @newChecklist
            checklist_ok = true
          end
          rescue ActiveRecord::RecordInvalid 
          # This is a NEW checklist, so it's acting like it's "new" - WRONG?
          puts "RESCUE 1"
          @checklist = @newChecklist
          @jobs = @newChecklist.jobs     
          checklist_ok = false
        end              
      elsif @testChecklist.changed_for_autosave? || params.has_key?(:new_jobs)
        puts "HERE 2"    
        # Associated Jobs have changed, so archive old checklists_jobs,
        # then set checklists_jobs based on params[:checklist][:jobs_attributes] and [:new_jobs]

        @checklist.set_new_jobs(params[:new_jobs])

        begin
          ActiveRecord::Base.transaction do
            @checklist.update_checklists_jobs!(jobs_attributes) if jobs_attributes
            @checklist.save!
            checklist_ok = true
          end
          rescue ActiveRecord::RecordInvalid      
          puts "RESCUE 2"
          @jobs = @checklist.unarchived_jobs
          checklist_ok = false
        end
      else
        checklist_ok = true # There were no changes to the Checklist or Jobs
      end
    else
      puts "HERE B"
      @checklist.set_new_jobs(params[:new_jobs])
      begin
        ActiveRecord::Base.transaction do
          @checklist.update_attributes(params[:checklist])
          checklist_ok = true
        end
        rescue ActiveRecord::RecordInvalid 
        puts "RESCUE B"
        @jobs = @checklist.jobs     
        checklist_ok = false
      end
    end

    respond_to do |format|
      if  checklist_ok
        format.html { redirect_to @checklist, notice: 'List successfully updated.' }
        format.json { head :no_content }
      else
        flash.now[:error] = 'There was a problem updating the List.'
        format.html { render action: "edit" }
        format.json { render json: @checklist.errors, status: :unprocessable_entity }
      end
    end
  end
end

Code - Checklist form

<%= form_for @checklist, :html => { :class => 'form-inline' } do |f| %>
  <div>
    <%= f.text_area :name, :rows => 1, :placeholder => 'Name the list...', :class => 'autoresizer checklist-name' %></br>
    <%= f.text_area :description, :rows => 1, :placeholder => 'Optional description...', :class => 'autoresizer' %>
  </div>

  <%= f.fields_for :jobs, :html => { :class => 'form-inline' } do |j| %>
    <%= render "job_fields", :j => j  %>
  <% end %>

  <span class="add-new-job-link"><%= link_to_add_fields "add a new job", f, :jobs %></span>
  <div class="form-actions">
    <%= f.submit nil, :class => 'btn btn-primary' %>
    <%= link_to 'Cancel', checklists_path, :class => 'btn' %>
  </div>

  <% unless @job_list.empty? %>
    <legend>Add jobs from the Job Bank</legend>

    <% @job_list.each do |job| %>
      <div class="toggle">
        <label class="checkbox text-justify" for="<%=dom_id(job)%>">
          <%= check_box_tag "new_jobs[]", job.id, false, id: dom_id(job) %><strong><%= job.name %></strong> <small><%= job.description %></small>
        </label>
      </div>
    <% end %>

    <div class="form-actions">
      <%= f.submit nil, :class => 'btn btn-primary' %>
      <%= link_to 'Cancel', checklists_path, :class => 'btn' %>
    </div>
  <% end %>
<% end %>

这篇关于Rails-如何在不使用accepts_nested_attributes_for的情况下管理嵌套属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆