Rails nested_attributes_for не работи при актуализация

Това е първият ми въпрос тук, така че се надявам да се получи. Търсих някой за подобен проблем, но досега не намерих нищо. Сигурен съм, че е нещо просто, но просто не мога да го видя в момента!

Опитвам се да запазя вложени атрибути за модел в rails 3.2.11, но операцията работи само при създаване на вложения обект, но не и при актуализиране.

Ето кода за 2 модела, с които работя

class Property < ActiveRecord::Base

  has_many :opening_times
  accepts_nested_attributes_for :opening_times, :allow_destroy => true
  attr_accessible :opening_times_attributes

...

class OpeningTime < ActiveRecord::Base
  belongs_to :property
  attr_accessible :start_date, :end_date

  attr_accessible :day, :start_time, :end_time
  attr_writer :day, :start_time, :end_time

  before_save :set_dates

...

  def set_dates
    day = Date.parse(@day)
    start_time = Time.parse(@start_time)
    end_time = Time.parse(@end_time)

    start_date = "#{day.day}/#{day.month}/#{day.year} #{start_time.hour}:#{start_time.min}"
    self.start_date = DateTime.parse(start_date)

    end_date = "#{day.day}/#{day.month}/#{day.year} #{end_time.hour}:#{end_time.min}"
    self.end_date = DateTime.parse(end_date)
  end

Така че, когато се опитам да създам време за отваряне чрез свойство, използвайки rails конзола, това работи:

1.9.3p125 :006 > p = Property.find(9)
1.9.3p125 :006 > p.opening_times_attributes = [{"day"=>"27/02/2013", "start_time"=>"11:30",     "end_time"=>"12:30", "_destroy"=>"false"}]
 => [{"day"=>"27/02/2013", "start_time"=>"11:30", "end_time"=>"12:30", "_destroy"=>"false"}] 
1.9.3p125 :007 > p.save!
 (0.1ms)  begin transaction
 Suburb Load (0.2ms)  SELECT "suburbs".* FROM "suburbs" WHERE "suburbs"."name" = 'BARREN GROUNDS' LIMIT 1
 SQL (0.6ms)  INSERT INTO "opening_times" ("end_date", "property_id", "start_date") VALUES (?, ?, ?)  [["end_date", Wed, 27 Feb 2013 12:30:00 UTC +00:00], ["property_id", 9], ["start_date", Wed, 27 Feb 2013 11:30:00 UTC +00:00]]
 (2.8ms)  commit transaction
 => true 

Но когато се опитам да актуализирам съществуващ вложен обект (предавайки id в хеша), той не прави нищо

1.9.3p125 :037 > p.opening_times
 => [#<OpeningTime id: 12, property_id: 9, start_date: "2013-02-27 11:00:00", end_date: "2013-02-27 13:00:00">] 
1.9.3p125 :038 > p.opening_times_attributes = [{"day"=>"27/02/2013", "start_time"=>"11:30", "end_time"=>"12:30", "_destroy"=>"false", "id"=>12}]
 => [{"day"=>"27/02/2013", "start_time"=>"11:30", "end_time"=>"12:30", "_destroy"=>"false", "id"=>12}] 
1.9.3p125 :039 > p.save!
(0.1ms)  begin transaction
Suburb Load (0.2ms)  SELECT "suburbs".* FROM "suburbs" WHERE "suburbs"."name" = 'BARREN GROUNDS' LIMIT 1
(0.1ms)  commit transaction
 => true 
1.9.3p125 :040 > exit

Въз основа на това, което прочетох досега (т.е. http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes), това трябва да работи и за двете операции.

Някаква идея какво правя грешно?

Благодаря!

[РЕДАКТИРАНЕ]

Въз основа на предложението на @jvnill изглежда, че обратното извикване на before_save не се извиква при актуализация. Добавих заобиколно решение за изрично извикване на set_dates, когато някое от полетата се актуализира и след това работи.

def day=(day)
  @day = day
  set_dates unless (@day.blank? || @start_time.blank? || @end_time.blank?)
end

def start_time=(start_time)
  @start_time = start_time
  set_dates unless (@day.blank? || @start_time.blank? || @end_time.blank?)
end

def end_time=(end_time)
  @end_time = end_time
  set_dates unless (@day.blank? || @start_time.blank? || @end_time.blank?)
end

Това не решава напълно проблема, тъй като сега валидирането не работи безпроблемно и изглежда, че трябва ръчно да върша работата, която AR трябва да върши.


person frankmt    schedule 18.02.2013    source източник
comment
предполагам, че изграждате началния и крайния час на обратното извикване set_dates. можете ли да проверите този метод и да видите, че актуализацията преминава през него без никакви проблеми?   -  person jvnill    schedule 18.02.2013
comment
Проверих и този метод не се извиква при актуализацията. Все пак преминава нормално при създаването (както в примера).   -  person frankmt    schedule 18.02.2013
comment
така че там е проблемът ти. можете ли да включите този метод във вашия въпрос?   -  person jvnill    schedule 18.02.2013
comment
не съм сигурен, че следвам, но добавих метода към въпроса. Благодаря   -  person frankmt    schedule 18.02.2013


Отговори (3)


Бих проверил дали можете да актуализирате дъщерния си модел, без първо да използвате вложените атрибути. Възможно е вашият код за потвърждение или обратно извикване да спира актуализацията и нищо не е наред с вашето nested_attributes обаждане.

И така, това работи ли?

> o = p.opening_times.first
> o.update_attributes({"day"=>"27/02/2013", "start_time"=>"11:30", "end_time"=>"12:30"})

Ако не, деактивирайте валидациите и обратните извиквания и вижте дали nested_attributes преминават правилно.

РЕДАКТИРАНЕ:

Добре, така че изглежда, че проблемът е с извикването на вложените атрибути. Какво ще стане, ако опитате това?

> p.update_attributes(opening_times_attributes: [{"day"=>"27/02/2013", "start_time"=>"11:30", "end_time"=>"12:30", "_destroy"=>"false", "id"=>12} ])

Обърнете внимание, че извиквам update_attributes и не настройвам opening_times_attributes

РЕДАКТИРАНЕ 2:

Ако можете да унищожите записа чрез вложени атрибути, подозирам, че нещо отхвърля вашите промени в дъщерния модел. Проверихте ли за грешки при валидирането? Опитвали ли сте да актуализирате само 1 атрибут?

person Dane O'Connor    schedule 18.02.2013
comment
(0.1ms) начало на транзакцията (0.3ms) АКТУАЛИЗИРАНЕ на open_times SET start_date = '2013-02-27 11:30:00.000000', end_date = '2013-02-27 12:30:00.000000' WHERE opening_times.id = 12 (2.4 ms) извършва транзакция - person frankmt; 18.02.2013
comment
Отново нищо..(0.1ms) начало на транзакцията Предградие Load (0.2ms) ИЗБЕРЕТЕ предградия.* FROM предградия WHERE suburbs.name = 'BARREN GROUNDS' LIMIT 1 (0.1ms) ангажиране на транзакция - person frankmt; 18.02.2013
comment
Можете ли да унищожите записа? - person Dane O'Connor; 18.02.2013
comment
Чешам се по главата... Ако деактивирате обратното извикване set_date, можете ли да актуализирате чрез вложени атрибути? - person Dane O'Connor; 18.02.2013
comment
Току-що актуализира въпроса. Ако принудя атрибутите да се актуализират, когато виртуалните се променят, тогава запазването работи, така че изглежда, че е свързано с това, когато се изпълнява обратното извикване на before_save. - person frankmt; 18.02.2013

Вдъхновен от отговорите по-горе, разбрах, че трябва изрично да кажете на AR, че дадено поле се променя, когато е зададен някакъв виртуален атрибут, в противен случай обратното извикване before_save никога не се извиква.

Не е страхотно, но решението, което открих за проблема, е да извикам {attr}_will_change! всеки път, когато е зададен виртуален атрибут:

def day=(val)
  unless val == self.day
    start_date_will_change!
    end_date_will_change!
  end
  @day = val
end

def start_time=(val)
  start_date_will_change! unless val == self.start_time
  @start_time = val
end

def end_time=(val)
  end_date_will_change! unless val == self.end_time
  @end_time = val
end

Благодаря на всички за отговорите!

person frankmt    schedule 18.02.2013

Мисля, че вече знам вашия проблем. обратните извиквания _save не се задействат, когато нищо не е променено в записа. тъй като вие извиквате before_save, без наистина да променяте нищо в родителския модел, обратното извикване не се задейства. опитайте да използвате before_validation вместо before_save.

person jvnill    schedule 18.02.2013
comment
Ако случаят е такъв, това може да е проблемът, но промяната му на before_validation също не работи. Опитах да го ъпдейтна заедно с нещо по модела и пак не става. - person frankmt; 18.02.2013
comment
Току-що актуализира въпроса. Ако принудя атрибутите да се актуализират, когато виртуалните се променят, тогава запазването работи, така че изглежда свързано с това, когато се изпълнява обратното извикване на before_save - person frankmt; 18.02.2013
comment
можете ли да опитате първия отговор на този въпрос? stackoverflow.com/ въпроси/11460667/ - person jvnill; 18.02.2013
comment
Благодаря! Това точно решение не работи (time_entry не съществува?), но реши проблема чрез извикване на {attr}_will_change за засегнатите полета във всеки виртуален инструмент за настройка на атрибути. Ще публикувам отговора на него, но за съжаление трябва да чакам 5 часа за него (нямам достатъчно репутация! :P) Благодаря много за помощта - person frankmt; 18.02.2013