Rails has_many,:through - 在创建关联对象时更新连接表列

时间:2013-07-09 20:26:49

标签: join ruby-on-rails-3.2 has-many-through

我的问题

创建核对清单时,如何为新核对表的作业设置初始“job_position”(checklists_jobs连接表中的列为has_may,:通过关联)?我最好的猜测是我应该使用回调(Checklists控制器中的after_filter?)。

我还需要将相同的方法应用于Update控制器操作,但我怀疑代码几乎完全相同。

用例

我正在以单一形式创建清单和关联的作业(任务)。在此表单中,用户可以创建新的清单(名称,描述),并创建新的作业(名称,描述)和/或从选择列表中选择现有的作业。

实际表单位于下面的“代码”部分,但这里是用户在创建新核对表时会看到的内容摘要:

  • 清单名称:
  • 清单说明(可选):

(“添加新工作”链接) - 如果点击链接,我使用JS将新作业(空白)附加到表单:

  • 工作名称:
  • 职位描述(可选):

从作业库添加作业:

  • (复选框)现有工作1
  • (复选框)现有职位2

创建核对清单(按钮)

在我的连接表(checklists_jobs)上,我有必要的checklist_id和job_id字段,但我还有一个“job_position”字段,它告诉我该清单上的工作的显示顺序。 (我将主要通过下面的Checklist.rb代码中显示的“unarchived_jobs”关联来完成此操作。)

它现在如何运作

现在,我没有在Checklist创建期间设置“job_position”字段,所以它只是null。用户可以通过对Checklists#Show页面上的核对表进行排序(创建核对表之后)来手动设置“job_position”。但是我想在创建核对表时为连接记录设置job_position属性,以确保在创建列表时保留/反映用户的实际定位(例如,他们添加的顺序)使用“添加新工作”链接的新工作。 注意:这是针对他们使用“添加新工作”链接添加的作业。从选择列表中选择的作业显然需要在创建核对表后由用户手动排序。

我该怎么做?

代码

checklist.rb

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

  has_many :checklists_jobs, :dependent => :destroy, :order => 'checklists_jobs.job_position'
  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'
end

job.rb

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

  has_many :checklists_jobs, :dependent => :destroy
  has_many :checklists, :through => :checklists_jobs
end

checklists_job.rb

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

  belongs_to :job
  belongs_to :checklist
  attr_accessible :job_position, :job_required
end

_form.html.erb(新核对清单表格)

<%= form_for @checklist, :html => { :class => 'form-inline' } do |f| %>
    <%= 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' %>

    <%= 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>

    <% 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 %>

1 个答案:

答案 0 :(得分:0)

感谢朋友的建议,我使用acts_as list gem解决了这个问题。尽管我需要一些自定义调整,但实施起来还是微不足道的。默认:列是'position',所以我不得不重写它。我还需要一个自定义范围,因为我正在使用这个用于has_many,:通过连接表,我在该表中有“存档”项目(我不想在更新时订购等)。

以下是进行这些调整所需的代码行:

<强> checklists_job.rb

acts_as_list :column => 'job_position', :scope => 'checklist_id = #{checklist_id} AND archived = false'

这只是添加到我最初包含在我的问题中的代码中。

#147 Sortable Lists (revised)简要提到了acts_as_list。提及的主要原因在于安装和使用的简便性。