我有 3 个型号
User
、Feature
、UserFeature
class User < ActiveRecord::Base
has_many :user_features
has_many :features, through: :user_features
end
class Feature < ActiveRecord::Base
has_many :user_features
has_many :users, through: :user_features
end
class UserFeature < ActiveRecord::Base
belongs_to :user
belongs_to :feature
end
我已经在数据库中创建了许多功能,并在使用以下代码创建用户时将功能与用户相关联
<%= form_for @user do |f| %>
// some user_fields
<% Feature.all.each do |feature| %>
<%= check_box "user[feature_ids][], feature.id %>
<% end %>
// submit button here
<% end %>
在我的
UserController
我有这个代码
class UserController < ApplicationController
def create
@user = User.new(permit_params)
@user.save
end
def update
@user = User.find(params[:id])
@user.update_attributes(permit_params)
end
private
def permit_params
params.require(:user).permit(:name, :email, user_feature_ids: [])
end
end
当我提交时,它将创建和更新用户,并在
UserFeature
表中为我检查过的那些功能进行条目。
更新用户时,如果我取消选中任何功能,那么它将从
UserFeature
中删除相关记录
这里没有任何问题,一切都按预期进行。
但现在我想在删除 user_feature 时执行一些活动。
为此我在 UserFeature 中编写了一个回调
after_destroy :some_activity
class UserFeature < ActiveRecord::Base
belongs_to :user
belongs_to :feature
after_destroy :some_activity
def some_activity
// some code
end
end
但它不起作用,当我检查为什么它在删除 user_feature 时不调用 destroy 调用时,我发现它会调用 SQL 查询,而不是在未选中的 user_feature 上调用
destroy
。
这就是为什么
after
或 before
destroy
回调不起作用。
任何人都可以告诉我,我如何在删除时执行任何活动
UserFeature
?
after_destroy
回调不会在delete
之后触发,因为这就是delete
的实现方式。引用自docs:
Active Record 对象未实例化,因此不会执行对象的回调,包括任何 :dependent 关联选项。 [...] 注意:虽然它通常比替代方案 #destroy 快得多,但跳过回调可能会绕过应用程序中确保引用完整性或执行其他重要工作的业务逻辑。
您可以在关联定义上使用
after_destroy
回调,而不是关联类上的 after_remove
回调,如 Rails Guide about Association Callbacks 中所述。
解决方案是不将代码设置为使用“自动删除连接模型”模式(因为此模式不会触发删除的回调)。
相反,使用
accepts_nested_attributes_for
声明将代码设置为标准嵌套资源,这将触发删除回调。
模型是:
class User < ActiveRecord::Base
has_many :user_features
accepts_nested_attributes_for :user_features, allow_destroy: true
end
class Feature < ActiveRecord::Base
has_many :user_features
end
class UserFeature < ActiveRecord::Base
belongs_to :user
belongs_to :feature
after_destroy :some_activity
def some_activity
# Some code here to be executed after the Object destruction
end
end
注:
through:
声明中需要has_many
,因为您不会使用它附带的getter/setter.feature_ids
。accepts_nested_attributes_for
选项的allow_destroy: true
声明。表格为:
<%= form_for @user do |user_form| %>
// Any User fields here
<%= user_form.fields_for :user_features, @user_features do |user_feature_form| %>
// The UserFeature check_box field:
<%= user_feature_form.check_box :_destroy,
{ checked: user_feature_form.object.persisted?,
class: 'my_class' },
checked_value = false,
unchecked_value = true %>
// Any other UserFeature fields here, i.e.:
<%= user_feature_form.number_field :quantity,
class: 'my_class' %>
<% end %>
// Submit button here
<% end %>
注:
.fields_for
方法生成 UserFeature
字段。:_destroy
。checked_value
必须是false
(如果选中,则不要删除)unchecked_value
必须是true
(如果未选中,则do删除)(基本)控制器是:
class UserController < ApplicationController
def create
@user = User.new(permit_params)
@user.save
end
def new # or: def edit
@user = User.find(params[:id])
Feature.all.each do |feature|
user_feature = @user.user_features.select {|u_f| u_f.feature_id == feature.id}[0]
# If no corresponding UserFeature exists, build it (= non-persisted, which will determine if the check_box is checked or not)
if user_feature.nil?
@user.user_features.build(feature_id: feature.id, quantity: nil)
end
end
# Optional: Sort (i.e. by Feature.name) to keep the list order fixed (pass this array to the form)
@user_features = @user.user_features.sort_by {|u_f| u_f.feature.name}
end
def update
@user = User.find(params[:id])
@user.update_attributes(permit_params)
end
private
def permit_params
params.require(:user).permit(:name, :email, user_features_attributes: [
:id, :quantity, :_destroy
])
end
end