ruby on rails - обновлять несколько записей одной модели и ее ассоциации в одной форме - PullRequest
1 голос
/ 20 марта 2012

У меня есть форма, которая позволяет пользователю обновлять несколько записей правил оповещения одновременно.Но каждая запись правила оповещения может содержать много уведомлений.Поэтому я хотел бы иметь возможность обновлять правила оповещений и соответствующие уведомления по электронной почте в одной форме.

Модели:

class AlertRule < ActiveRecord::Base

    has_many :notification_emails, :dependent => :destroy
    accepts_nested_attributes_for :notification_emails, :reject_if => lambda { |notification| notification[:email].blank? }
end

class NotificationEmail < ActiveRecord::Base
  belongs_to :alert_rule
end

На одном из моих контроллеров я отправляю массив предупреждений в форму:

  def alerts_config

      //more code
      @alert_rules = alert_rules.flatten
      @alert_rules.each { |a| a.notification_emails.build }

      render :partial => 'home/alerts_config', :layout => false
  else

Затем в своей форме я хочуразрешить пользователю обновлять правила оповещений и соответствующие уведомления по электронной почте:

    = form_for @alert_rules, :url => '/home/save_alerts_config/' + @unit.id.to_s, :remote => true, :class => 'ajaxForm' do |f|
  %table.scrollTable{:cellspacing => "0", :width => "740px", :style => "border-collapse: collapse; border-spacing: 0;"}
    %thead.fixedHeader
      %tr
        %th Alerts
        %th Enable
        %th Primary Email
        %th Notification Emails
        %th
    %tbody.scrollContent
      - for rule in @alert_rules
        %tr
          %td= rule.alert_code.name
          %td= check_box_tag "enabled_ids[]", rule.id
          %td= f.text_field :email, :value => rule.email, :index => rule.id
          %td.fields
            = f.fields_for :notification_emails, rule.notification_emails do |notification_builder|
              = notification_builder.text_field :email
              = notification_builder.hidden_field :_destroy              
              = link_to_function 'Remove Notification', 'remove_notifications(this)'
  .save_panel
    = submit_tag "Save", :class => 'submit myButton'

Когда я отправляю на сервер, я получаю следующее:

Started POST "/home/save_alerts_config/6243" for 127.0.0.1 at 2012-03-20 17:49:06 -0400
  Processing by HomeController#save_alerts_config as JS
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"NPwuKuWippYjm2tJcfQI+/x9oEBwcR2rxcfpZMTO/Qo=", "enabled_ids"=>["51"], "alert_rule"=>{"51"=>{"email"=>"hythy"}, "notification_emails_attributes"=>{"0"=>{"email"=>"rtyrytry", "_destroy"=>"false"}, "1"=>{"email"=>"", "_destroy"=>"false"}, "2"=>{"email"=>"", "_destroy"=>"false"}, "3"=>{"email"=>"", "_destroy"=>"false"}, "4"=>{"email"=>"", "_destroy"=>"false"}, "5"=>{"email"=>"", "_destroy"=>"false"}, "6"=>{"email"=>"", "_destroy"=>"false"}, "7"=>{"email"=>"", "_destroy"=>"false"}, "8"=>{"email"=>"", "_destroy"=>"false"}, "9"=>{"email"=>"", "_destroy"=>"false"}, "10"=>{"email"=>"", "_destroy"=>"false"}, "11"=>{"email"=>"", "_destroy"=>"false"}, "12"=>{"email"=>"", "_destroy"=>"false"}, "13"=>{"email"=>"", "_destroy"=>"false"}, "14"=>{"email"=>"", "_destroy"=>"false"}, "15"=>{"email"=>"", "_destroy"=>"false"}, "16"=>{"email"=>"", "_destroy"=>"false"}, "17"=>{"email"=>"", "_destroy"=>"false"}, "18"=>{"email"=>"", "_destroy"=>"false"}, "19"=>{"email"=>"", "_destroy"=>"false"}, "20"=>{"email"=>"", "_destroy"=>"false"}, "21"=>{"email"=>"", "_destroy"=>"false"}, "22"=>{"email"=>"", "_destroy"=>"false"}, "23"=>{"email"=>"", "_destroy"=>"false"}, "24"=>{"email"=>"", "_destroy"=>"false"}, "25"=>{"email"=>"", "_destroy"=>"false"}, "26"=>{"email"=>"", "_destroy"=>"false"}, "27"=>{"email"=>"", "_destroy"=>"false"}, "28"=>{"email"=>"", "_destroy"=>"false"}, "29"=>{"email"=>"", "_destroy"=>"false"}, "30"=>{"email"=>"", "_destroy"=>"false"}, "31"=>{"email"=>"", "_destroy"=>"false"}, "32"=>{"email"=>"", "_destroy"=>"false"}, "33"=>{"email"=>"", "_destroy"=>"false"}, "34"=>{"email"=>"", "_destroy"=>"false"}, "35"=>{"email"=>"", "_destroy"=>"false"}, "36"=>{"email"=>"", "_destroy"=>"false"}, "37"=>{"email"=>"", "_destroy"=>"false"}, "38"=>{"email"=>"", "_destroy"=>"false"}, "39"=>{"email"=>"", "_destroy"=>"false"}, "40"=>{"email"=>"", "_destroy"=>"false"}}, "52"=>{"email"=>"yutu"}, "53"=>{"email"=>"ytuytu"}, "54"=>{"email"=>""}, "55"=>{"email"=>""}, "56"=>{"email"=>""}, "57"=>{"email"=>""}, "58"=>{"email"=>""}, "59"=>{"email"=>""}, "60"=>{"email"=>""}, "61"=>{"email"=>""}, "62"=>{"email"=>""}, "63"=>{"email"=>""}, "64"=>{"email"=>""}, "65"=>{"email"=>""}, "66"=>{"email"=>""}, "67"=>{"email"=>""}, "68"=>{"email"=>""}, "69"=>{"email"=>""}, "70"=>{"email"=>""}, "71"=>{"email"=>""}, "72"=>{"email"=>""}, "73"=>{"email"=>""}, "74"=>{"email"=>""}, "75"=>{"email"=>""}, "76"=>{"email"=>""}, "77"=>{"email"=>""}, "78"=>{"email"=>""}, "79"=>{"email"=>""}, "80"=>{"email"=>""}, "81"=>{"email"=>""}, "82"=>{"email"=>""}, "83"=>{"email"=>""}, "84"=>{"email"=>""}, "85"=>{"email"=>""}, "86"=>{"email"=>""}, "87"=>{"email"=>""}, "88"=>{"email"=>""}, "89"=>{"email"=>""}, "90"=>{"email"=>""}, "91"=>{"email"=>""}}, "commit"=>"Save", "id"=>"6243"}

Этого не должно быть.Как показано выше в контроллере, я создаю только одно уведомление для каждого предупреждения, поэтому, почему он отправляет все уведомления обратно, как если бы они все были связаны с первым предупреждением, когда я проверяю только первое предупреждение, мне неведомо.

обновление: даже когда я использовал создать!вместо сборки для фактической записи связанной записи все равно возникла та же проблема: не удалось получить идентификатор предупреждения внутри атрибута name поля ввода messages_email

Спасибо за ответ

1 Ответ

0 голосов
/ 23 марта 2012

Тебе это не понравится.Но это то, что я сделал, чтобы заставить его работать ...

Информация, основанная на этом вопросе stackoverflow и находился под сильным влиянием этот довольно удивительный ответ Я могу ответитьэто:

models / alert_rules_set.rb

# NOTICE: I'm not inheriting from ActiveRecord::Base
class AlertRulesSet
  extend ActiveModel::Naming
  include ActiveModel::Conversion

  attr_accessor :alert_rules

  def alert_rules_attributes=(attributes)
    # I'm tricking "fields_for" here.
    # This should never actually be called
    raise
  end

  # Strip fields_for indexes and return an array
  def self.alert_rules_from(collection_set_hash)
    collection_set_hash[:alert_rules_attributes].values
  end

  def persisted?
    false
  end
end

Контроллер

def alerts_config
  //more code
  @alert_rules_set = AlertRulesSet.new
  @alert_rules_set = AlertRules.all # Or Whatever
  @alert_rules_set.alert_rules.each { |a| a.notification_emails.build }

  render :partial => 'home/alerts_config', :layout => false
end

def update_alerts_config
  @alert_rules = AlertRulesSet.alert_rules_from(params[:alert_rules_set])

  # Save Logic here
end

Форма

= form_for @alert_rules_set, :url => '/home/save_alerts_config/' + @unit.id.to_s, :remote => true, :class => 'ajaxForm' do |f|
  %table.scrollTable{:cellspacing => "0", :width => "740px", :style => "border-collapse: collapse; border-spacing: 0;"}
  %thead.fixedHeader
    %tr
      %th Alerts
      %th Enable
      %th Primary Email
      %th Notification Emails
      %th
  %tbody.scrollContent
    = f.fields_for :alert_rules do |alert_rules_builder|
      %tr
        %td= rule.alert_code.name
        %td= check_box_tag "enabled_ids[]", rule.id
        %td= alert_rules_builder.text_field :email, 
                         :value => rule.email, :index => rule.id
        %td.fields
          = alert_rules_builder.fields_for :notification_emails do |notification_builder|
          = notification_builder.text_field :email
          = notification_builder.hidden_field :_destroy              
          = link_to_function 'Remove Notification',
                'remove_notifications(this)'.save_panel
= submit_tag "Save", :class => 'submit myButton'

Вот ссылка на ветку git , где я работал с другим приложением и , сравнение .

Этопсих!Что происходит?

Так исторически ActionPack обычно довольно тесно связаны с ActiveRecord .Это не очень хорошая идея по нескольким причинам, поэтому начиная с Rails 3.0 ActiveModel был представлен.К сожалению, это один из тех случаев, которые, возможно, могли бы быть лучше.

ActiveModel

ПРИМЕЧАНИЕ: кое-что из этого меняется, в частности, с это Rails 4 commit

В общем, я реализую минимум, чтобы этот случай работал.Помимо директив extend и include, единственное, что необходимо для того, чтобы любой другой класс ActiveModel работал хорошо в этом случае, это persisted?.

Что касается всего остального ...

attr_accessor :alert_rules
Это просто данные alert_rules.Это то, что fields_for будет использовать для заполнения полей существующими данными.

alert_rules_attributes=(attributes)
Это своего рода особенное.Это то, что fields_for проверяет, когда он хочет знать, когда отображать коллекцию или нет.Без этого вы просто получите одно вложенное поле (т. Е. [alert_rule][notification_emails_attributes]) вместо набора вложенных полей (т. Е. [alert_rule][notification_emails_attributes][1]).

self.alert_rules_from(collection_set_hash)
Это более запутанная часть.Таким образом, в основном, когда вы отправляете некоторые вложенные атрибуты, они на самом деле находятся в Hash с индексным ключом.

nested_model_attributes = {
    "0"=>{"attribute"=>"This", "id"=>"0"},
    "1"=>{"attribute"=>"That"}
}

Этот метод - быстрый способ избавиться от этого и получить полезные данные ( Это происходит и в ActiveRecord ).Я подумал, что, вероятно, было бы легче манипулировать данными, чем пытаться сделать магию сохранения в классе AlertRuleSet (но вы могли бы).

...