2015-07-26 2 views
1

Почему обратный вызов before_update с использованием обновленных параметров? Не следует ли использовать исходные параметры?Обратный вызов Before_update вызывается с использованием обновленных атрибутов

У меня есть Round модель, которая belongs_to в Case модели (Casehas_manyRounds)

Некоторые из атрибутов Case вычисляются через обратные вызовы Round «s. Например, когда есть новый Round, который был выигрышем, атрибут wins Case увеличивается на 1.

У меня возникла проблема с обратными вызовами теперь, когда мне нужно обновить свой случай. У меня есть метод add_round и delete_round в модели Round, которые добавляют и удаляют атрибуты заданного раунда соответственно.

Чтобы уточнить, delete_round и add_round фактически не удалить или создать круглую, они удалить или добавить статы раунда к случаям атрибуты

Вот моя круглая модель:

class Round < ActiveRecord::Base 

    belongs_to :case 

    after_save  :add_round 
    before_update :delete_round 
    before_destroy :delete_round 

    private 
    # Adds this Round's stats to its Case 
    def add_round 
     self.case.add_round(self) 
    end 

    # Deletes this Round's stats from its Case 
    def delete_round 
     self.case.delete_round(self) 
    end 
end 

а вот случай модель:

class Case < ActiveRecord::Base 

    has_many :rounds,  dependent: :destroy 

    def add_round(round) 
    if round.win? 
     update_attribute(wins: wins + 1) 
    else 
     update_attribute(losses: losses + 1) 
    end 
    update_attribute(win_percentage: 100 * wins/(wins + losses) 
    end 

    def delete_round(round) 
    if round.win? 
     update_attribute(wins: wins - 1) 
    else 
     update_attribute(losses: losses - 1) 
    end 
    update_attribute(win_percentage: 100 * wins/(wins + losses) 
    end 
end 

и схема:

create_table "cases", force: :cascade do |t| 
    t.datetime "created_at",        null: false 
    t.datetime "updated_at",        null: false 
    t.integer "wins",      default: 0 
    t.integer "losses",     default: 0 
    t.float "win_percentage",   default: 0.0 
end 

create_table "rounds", force: :cascade do |t| 
    t.integer "case_id" 
    t.boolean "win" 
    t.datetime "created_at",     null: false 
    t.datetime "updated_at",     null: false 
end 

Вот грубый псевдокод иш пример того, что я хочу, чтобы это произошло:

case = Case.create(wins: 0, losses: 0, win_percentage: 0) 
round = Round.create(win: true, case_id: case.id) 

# add_round is called 

case.wins = 1 
case.losses = 0 
case.win_percentage = 100 

round.update_attribute(win: false) 

# before that parameter is updated, delete_round is called on the round with the old attributes 

case.wins = 0 
case.losses = 0 
case.win_percentage = 0 

# after that parameter is updated, add_round is called on the updated round 

case.wins = 0 
case.losses = 1 
case.win_percentage = 0 

round.destroy 

# delete_round is called 

case.wins = 0 
case.losses = 0 
case.win_percentage = 0 

После того, как они обновляются, добавьте раунд с новыми параметрами

По какой-то причине, before_update получает отправленные обновленные атрибуты. Почему это происходит? Как мне изменить обратные вызовы?

+0

Я не мог понять, что вы хотите удалить раунд, когда дело обновляется? или есть еще вещи? – juanpastas

+0

или удалить раунд, когда раунд обновлен ??? – juanpastas

+0

Я хочу удалить старую версию раунда из статистики дел, а затем добавить новую версию. Другими словами, обновление раунда от выигрыша до потери - это то же самое, что и удаление этой победы, а затем добавление убытка. Должно быть яснее: delete_round и add_round не удаляют фактические раунды, они удаляют статистику этого раунда из футляра –

ответ

0

Вы вызываете delete_round как для before_validation, так и before_save. before_save вызывается в обоих случаях: при обновлении и при создании. Если вы хотите вызвать delete_round только на обновления, вы должны сказать:

before_update :delete_round

или

before_save :delete_round, on: :update

Обновлено

Проблема заключается в том, как ваш звонок ваши обратные вызовы и логика ваших методов:

after_save :add_round before_update :delete_round

Только для создания вашего 'after_save: add_round' будет вызываться без вызова delete_round в before_update. И они будут вызываться на одном объекте в одной транзакции. Поэтому в большинстве случаев сначала вы вызываете delete_round, а затем add_round. И поскольку они выполняют обратные операции, вы не получите никаких изменений.

То, что вы на самом деле хотите сделать, это вызвать обработку ваших результатов только один раз, так что ваш метод будет что-то вроде этого:

def process_round(round) result = round.win? ? 1 : -1 update_attribute(wins: wins + result) update_attribute(losses: losses - result) end

И я сомневаюсь, что тип обратного вызова изменяет много, так что вы может after_save только для примера:

В вашем Круглом классе: after_save :process_round

Обновлено

Другой способ отделить add_round и delete_round действия является использование условных обратных вызовов, как это:

класс Круглый < ActiveRecord :: Base

belongs_to: случай

before_update: add_round, если: победа ?

before_update: delete_round, если: win?

before_destroy: delete_round

частный

# Adds this Round's stats to its Case 
def add_round 
    self.case.add_round(self) 
end 

# Deletes this Round's stats from its Case 
def delete_round 
    self.case.delete_round(self) 
end 

конец

Это, вероятно, что вам действительно нужно.

+0

К сожалению, в моем посте была опечатка. Обновлено, чтобы показать, что это такое. В любом случае, когда я вызываю ': delete' round на' before_update', переданные ему параметры являются обновленными. Поэтому вместо удаления старого раунда он удаляет новый, а затем добавляет новый, поэтому ничего не меняется. –

+0

С вашим псевдокодом сложно сказать. Можете ли вы разместить свой код для моделей? Только модели с обратными вызовами и их реализация. –

+0

Конечно, я обновил сообщение, чтобы включить соответствующую информацию в модели и схему –

0

Другое решение, которое я создал, в дополнение к ответу уже предусмотрено:

# Deletes this Round's stats from its Case 
    def delete_round 
    round = Round.new 
    round.assign_attributes(self.attributes) 
    round.assign_attributes(self.changed_attributes) 
    self.case.delete_round(round) 
    end 
Смежные вопросы