Update obiektu nadrzednego

Mam w aplikacji dwa modele

class Value
	has_many :properties
	field :property_price_cache
end

class Property
	belongs_to :value
	field :price # decimal
end

p = Property.new(:price => 1000.0)
p.save 

chce zeby w trakcie tego save odswierzyl sie p.value.property_price_cache. Powinien on zawsze “zsumowac wszystkie Property i ich :price” i przypisac do tego pola. Oczywiscie musi dzialac na save i update i destroy

Przyklad:

[code=ruby]p1 = Property.create(:price => 1000.0)
p2 = Property.create(:price => 3000.0)

p2.value.properties_price_count => 4000

p1.update_attributes(:price => 2000)

p1.value.properties_price_count => 5000

[p1,p2].each do |object|
object.update_attributes(:price => 0)
end

p1.value.properties_price_count => 0[/code]
Doszedlem do tego rozwiazania ale jest strasznie “hacky”

class Property < ActiveRecord::Base

  after_save :update_price_cache
  before_destroy :delete_price_cache

  private

  def update_price_cache
    Value.where(:id => self.value.id).update_all(:properties_price_count => self.value.properties.sum(:price))
  end

  def delete_price_cache
    v = Value.where(:id => self.value.id)
    v.update_all(:properties_price_count => v.first.properties_price_count - self.price)
  end

end

ps:
rails 3.0.7
postgresql

Na moje oko rozwiązanie nie jest złe. ‘Hacky’ może być tylko to, że modyfikacja jest robiona bezpośrednio SQL-em, a nie przez coś w stylu ‘self.value.update_properties_count_cache’.

Jeśli zależy Ci na wydajności, to taki SQL może być OK, ale jeśli czytelność jest ważniejsza, to raczej metody konkretnych obiektów chyba będą lepsze.

Coś w stylu self.value.notify_about_value_changes if value_changed?, a już Value samo sobie wyliczy sumę swoich properties. Oczywiście w odpowiednich miejscach musisz zadbać o aktualność obiektów (np. przez reload) - model siedzący w pamięci niekoniecznie zauważy zmianę wprowadzoną w bazie danych przez którąś swoją kopię.

Generalnie Property nie powinno mieszać się w wewnętrzne sprawy obiektu Value. Kultura przede wszystkim - nie włazi się do cudzego domu, żeby komuś wyłączyć radio, tylko się grzecznie prosi, żeby sam raczył to zrobić. :slight_smile: