Rails 3. Сохранение ассоциации, атрибуты которой представлены значением объекта - PullRequest
2 голосов
/ 07 ноября 2010

Почему я не могу сохранить изменения в связанном объекте, сохраняя текущий объект, если атрибуты первого представлены объектом значения?

Например, у меня есть простое приложение для электронной коммерции. Для манипулирования клиентами

используется модель Client

Модель клиента


# db/migrate/<...>_create_clients.rb

class CreateClients < ActiveRecord::Migration
  def self.up
    create_table :clients do |t|
      t.string :name
    end
  end

  def self.down
    drop_table :clients
  end
end


# app/models/client.rb

Client < ActiveRecord::Base
  has_one :balance
end

и связанная с ним Баланс модель для удержания их балансов.

Модель баланса


# db/migrate/<...>_create_balances.rb

class CreateBalances < ActiveRecord::Migration
  def self.up
    create_table :balances do |t|
      t.integer    :amount
      t.string     :currency
      t.references :client
    end
  end

  def self.down
    drop_table :balances
  end
end


# app/models/balance.rb

class Balance < ActiveRecord::Base
  belongs_to :client

  composed_of :money,
    :mapping     =>
      [%w{amount cents}, %w{currency currency_as_string}],
    :constructor =>
      ->(amount, currency) { Money.new(amount || 0, currency || 'RUB') }
end

Модель Balance использует объект Money из внешней библиотеки с именем Money . Объект представляет атрибуты модели количество и валюта , добавляя к модели полезные методы для управления этими атрибутами.

Gemfile

<code>
# Gemfile

gem 'money'

У меня есть некоторые данные в seed.rb .

Семена


# db/seeds.rb

elena = Client.create(:name => 'Elena')
elena.build_balance.money = Money.new(0, 'RUB')
elena.save

Когда я пытаюсь изменить баланс клиента, он не изменяется, несмотря на метод save текущего объекта, возвращает true .


>> elena = Client.find_by_name('Elena')
=> #<Client id: 1, name: "Elena">

>> elena.balance
=> #<Balance id: 1, amount: 0, currency: "RUB", client_id: 1>

>> elena.balance.money
=> 0.00

>> elena.balance.money += Money.new(50000, 'RUB')
=> 500.00

>> elena.save
=> true

# log/development.log
# no changes

Однако я могу сохранить изменения двумя способами.

1.

<code>
>> elena = Client.find_by_name('Elena')
=> #<Client id: 1, name: "Elena">

>> balance = Balance.find(elena.id)
=> #<Balance id:1 , amount: 0, currency: "RUB", client_id: 1>

>> balance.money += Money.new(50000, 'RUB')
=> 500.00

>> balance.save
=> true

# log/development.log
# UPDATE "balances" SET "amount" = 50000 WHERE ("balances"."id" = 1)

2.


>> elena = Client.find_by_name('Elena')
=> #<Client id:1, name:"Elena">

>> elena.balance.money += Money.new(50000, 'RUB')
=> 500.00

>> elena.balance.save
=> true

# log/development.log
# UPDATE "balances" SET "amount" = 50000 WHERE ("balances"."id" = 1)

Несмотря на то, что я могу сохранить изменения баланса с помощью вышеупомянутых способов, я хотел бы знать, почему я не могу сделать это с помощью «традиционного» * ​​1078 * .save.

Спасибо.

Debian GNU / Linux 5.0.6;

Рубин 1.9.2;

Ruby on Rails 3.0.1;

Деньги 3.1.5.

1 Ответ

1 голос
/ 07 ноября 2010

Это достигается следующим образом:

# app/models/client.rb

Client < ActiveRecord::Base
  has_one :balance, :autosave => true
end

Проблема с ассоциацией ранее заключалась в том, что когда вы вызывали elena.save, он только смотрел, изменился ли объект Client.Если вы выполнили проверку, подобную этой, в первом примере:

>> elena.changed?
=> false
>> elena.balance.changed?
=> true

Если вы не сообщите ActiveRecord для проверки связанных моделей, это будет лениво и игнорировать изменения при сохранении родительской модели.

...