使用accepts_nested_attributes_for修改连接模型上的属性

发布于 2024-09-26 06:41:37 字数 2441 浏览 5 评论 0原文

简单地说,联系人可以有各种关联的时间窗口,这些时间窗口可能会或可能不会作为计划活动。也就是说:

模型

class Contact < ActiveRecord::Base
  has_many :schedules
  has_many :time_windows, :through => :schedules
  accepts_nested_attributes_for :schedules, :allow_destroy => true
end

class TimeWindow < ActiveRecord::Base
  has_many :schedules
  has_many :contacts, :through => :schedules
end

class Schedule < ActiveRecord::Base
  belongs_to :contact
  belongs_to :time_window
end

视图

  <% TimeWindow.all.each do |tw| %>
    <% schedule = Schedule.find_by_contact_id_and_time_window_id(@contact.id, tw.id)
       schedule ||= Schedule.new %>
    <p>
      <%= f.label tw.description %>
      <%= hidden_field_tag "contact[schedules_attributes][][id]", schedule.id %>
      <%= check_box_tag "contact[schedules_attributes][][time_window_id]",
 tw.id, @contact.time_windows.include?(tw) %>
      <%= check_box_tag "contact[schedules_attributes][][active]", nil,
 schedule.active %>
    </p>
  <% end %>

这提交了类似这样的内容:

Parameters: { "commit" => "Update", "contact" => {
  "group_ids" => ["2"], "enabled" => "1",
  "schedules_attributes" => [ { "time_window_id"=>"1", "id"=>"46"},
   { "time_window_id" => "2", "id" => "42", "active" => "on" },
   { "time_window_id" => "3", "id" => "43"},
   { "time_window_id" => "4", "id" => "44", "active" => "on"}],
  "last_name" => ... 

控制器中的更新操作基本上是库存的,除了处理我使用“编码的另一个相关模型的另一个实例”处理多个模型”示例来自《Advanced Rails Recipes》一书。

根据 此 API 文档,我认为上述内容应该有效。然而,有关时间表的任何内容都没有得到更新。这显示在服务器日志中:

  [4;35;1mSchedule Update (0.2ms)[0m   [0mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 42[0m
  [4;36;1mSchedule Update (0.1ms)[0m   [0;1mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 44[0m

(NetBeans 在输出中给了我那些愚蠢的“[0m”。我不知道那里出了什么问题。)

SQL 显示“active”布尔字段被设置为 0在哪里检查的。我如何才能正确设置活动位?

作为后续,我将如何组织它以完全摆脱计划“连接”?我想我需要从表单中提交带有时间表的 :_delete ,但是当涉及复选框时,我将如何有条件地执行此操作?

感谢您提供的任何帮助。 Rails 对我来说是一个广阔的课题,我想“正确”地做它。我真的很接近这里,但必须有一种方法可以使它不仅正确而且优雅。视图代码对于合适的 Rails 来说太麻烦了。 ;-)

Simply, a Contact can have various associated Time Windows, which may or may not be Active as a Schedule. To wit:

Models

class Contact < ActiveRecord::Base
  has_many :schedules
  has_many :time_windows, :through => :schedules
  accepts_nested_attributes_for :schedules, :allow_destroy => true
end

class TimeWindow < ActiveRecord::Base
  has_many :schedules
  has_many :contacts, :through => :schedules
end

class Schedule < ActiveRecord::Base
  belongs_to :contact
  belongs_to :time_window
end

View

  <% TimeWindow.all.each do |tw| %>
    <% schedule = Schedule.find_by_contact_id_and_time_window_id(@contact.id, tw.id)
       schedule ||= Schedule.new %>
    <p>
      <%= f.label tw.description %>
      <%= hidden_field_tag "contact[schedules_attributes][][id]", schedule.id %>
      <%= check_box_tag "contact[schedules_attributes][][time_window_id]",
 tw.id, @contact.time_windows.include?(tw) %>
      <%= check_box_tag "contact[schedules_attributes][][active]", nil,
 schedule.active %>
    </p>
  <% end %>

This submits something like this:

Parameters: { "commit" => "Update", "contact" => {
  "group_ids" => ["2"], "enabled" => "1",
  "schedules_attributes" => [ { "time_window_id"=>"1", "id"=>"46"},
   { "time_window_id" => "2", "id" => "42", "active" => "on" },
   { "time_window_id" => "3", "id" => "43"},
   { "time_window_id" => "4", "id" => "44", "active" => "on"}],
  "last_name" => ... 

The update action in the controller is basically stock, except to handle another instance of another related model which I coded using the "Handling Multiple Models" example from the Advanced Rails Recipes book.

According to this API doc, I think the above ought to work. However, nothing about the Schedules is getting updated. This shows up in the server log:

  [4;35;1mSchedule Update (0.2ms)[0m   [0mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 42[0m
  [4;36;1mSchedule Update (0.1ms)[0m   [0;1mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 44[0m

(NetBeans is giving me those stupid "[0m"'s in the output. I don't know what's wrong there.)

The SQL shows that the "active" boolean field is getting set to 0 where checked. How do I get this to correctly set the active bit?

As a followup, how would I organize this to get rid of the Schedule "connection" at all? I'm thinking I need to submit a :_delete with the Schedule from the form, but how would I do that conditionally when a checkbox is involved?

Thanks for any help you can provide. Rails is turning out to be a vast subject for me, and I want to do it "right." I'm really close here, but there's got to be a way to make this -- not just correct -- but elegant. The view code just feels way too cumbersome to be proper Rails. ;-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

伊面 2024-10-03 06:41:37

我一直在尝试不同的方法来解决这个问题,最后我想出了这个,它很有效。大多。唯一的问题是它不能处理每个“时间窗口”没有“时间表”的情况。表单将呈现,我将得到一个禁用的复选框(以防止我尝试删除不存在的东西),但我没有办法将其添加回来,并且在没有它的情况下提交会抛出参数hash (并导致 Rails 给出“Expected Hash (got Array)”错误)

<% TimeWindow.all.each do |tw| %>
  <% schedule = Schedule.find_by_contact_id_and_time_window_id(@contact.id, tw.id)
     schedule ||= Schedule.new %>

<% f.fields_for "schedules_attributes[]", schedule do |sf| %>
  <p>
    <%= sf.label tw.description %>
    <%= sf.hidden_field :id %>
    <%= sf.check_box :_destroy, :disabled => schedule.new_record? %>
    <%= sf.check_box :active %>
  </p>
<% end %>

<% end %>

请注意,“schedules_attributes[]”数组会自动为您的 HTML 中的大括号内提供一个现有 ID(这很好),但是 _attributes hash 需要一个“id”以及其他属性,以便理解子哈希。

我在这里学到的重要教训之一是,“check_box_tag”方法并没有(似乎)为我提供了一个配对的隐藏字段,供 Rails 在未检查的情况下进行解析。我早就料到会这样。手动添加一个会造成混乱,这导致我最终屈服于“fields_for”方法,并在找到合适的语法以从中获得我想要的东西之前尝试了许多化身。

我意识到我的模型在这个设置中不太合适,所以我要改变它,但我非常接近这个答案,我想至少达到能够看到结局的程度在我继续前进之前。

I've kept trying different approaches to this problem, and I've come up with this, which works. Mostly. The only problem is that it doesn't handle NOT having a "Schedule" for each "Time Window". The form will render, and I'll get a disabled check_box (to prevent me from trying to delete something that isn't there), but I don't have a way to add it back, and submitting without it throws off the params hash (and causes Rails to give me an "Expected Hash (got Array)" error)

<% TimeWindow.all.each do |tw| %>
  <% schedule = Schedule.find_by_contact_id_and_time_window_id(@contact.id, tw.id)
     schedule ||= Schedule.new %>

<% f.fields_for "schedules_attributes[]", schedule do |sf| %>
  <p>
    <%= sf.label tw.description %>
    <%= sf.hidden_field :id %>
    <%= sf.check_box :_destroy, :disabled => schedule.new_record? %>
    <%= sf.check_box :active %>
  </p>
<% end %>

<% end %>

Note that the "schedules_attributes[]" array will automatically give you an existing ID within the braces in your HTML (which is nice), but the _attributes hash is expecting an "id" alongside the other attributes in order to make sense of the sub-hashes.

One of the big lessons I've learned here is that the "check_box_tag" method doesn't (seem to) give me a paired-up hidden field for Rails to parse in the unchecked case. I would have expected this. Adding one in by hand made a mess, which led me to finally giving into the "fields_for" method, and trying many incarnations before finding the appropriate syntax to get what I wanted out of it.

I've realized that my model isn't quite appropriate in this setup, so I'm going to change it, but I was so close to this answer, I wanted to at least get to the point of being able to see the end before I moved on.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文