为什么before_update回调使用更新的参数?它不应该使用原始的参数吗?
我有一个Round
模型belongs_to
Case
模型(Case
has_many
Rounds
)
Case
的部分属性是通过Round
的回调计算出来的。例如,当有一个新的Round
获胜时,Case
的wins属性会增加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 :(得分:0)
您为before_validation和before_save调用delete_round。在两种情况下调用before_save:on update和on create。 如果你只想在更新时调用delete_round,你必须告诉:
before_update :delete_round
或
before_save :delete_round, on: :update
<强>更新强>
问题在于你调用回调的方式和方法的逻辑:
after_save :add_round
before_update :delete_round
只有在创建你的'after_save:add_round'时才会调用before_update而不调用delete_round。并且它们将在单个事务中调用相同的对象。因此,在大多数情况下,首先您将调用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为例:
在你的Round课程中:
after_save :process_round
<强>更新强>
分离add_round和delete_round动作的另一种方法是使用这样的条件回调:
class Round&lt;的ActiveRecord ::基
belongs_to:case
before_update:add_round,if:win?
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
端
这可能是你真正需要的。
答案 1 :(得分:0)
我创建的另一个解决方案,除了已经提供的答案之外:
app.directive('myDirective', [ '$compile',
function ($compile) {
function postLink(scope, element, attrs) {
// Here, both the DOM and the scope are available, so
// you can extend the DOM with templates compiled against the scope
// if you are using <my-directive template="button.template"></my-directive>
var template = scope.$eval(attrs.template);
// if you are using <my-directive template="{{ button.template }}"></my-directive>
var template = attrs.template; // interpolation is already processed against the scope
// compile the template and append to existing DOM
element.append($compile(template || defaultTemplate)(scope));
}
function template(element, attrs) {
// Here, you cannot evaluate attrs.template against the scope
// because the scope does not yet exist! Only the DOM is
// available (you can use the raw values of attributes if needed)
return '<div></div>';
}
return {
template: template,
link: postLink
}
}
])