Rails 5 before_destroy回调:前置或不带失败

时间:2018-03-30 15:54:42

标签: ruby-on-rails callback ruby-on-rails-5.1

我知道在Rails 5中before_destroy回调不会考虑false返回值,而是考虑throw(:abort)。您可以在herethere中找到有关Rails API docsRails Guides的更多信息。

prepend选项在以下情况下仍不起作用。 我需要阻止删除以下模型中的最后一条记录:

class ShopLanguage < ApplicationRecord
  belongs_to :shop
  belongs_to :language
  has_many :descriptions, inverse_of: :shop_language, dependent: :destroy

  validates :language, :shop, presence: true
  validates :language, uniqueness: { scope: :shop, message: "already defined for this shop"}

  before_destroy :check_last_language, prepend: true


  private

  def check_last_language 
    if shop.languages.count < 2
      errors[:base] << "You can't delete the last language"
      false
    end
  end
end

这是Shop模型:

class Shop < ApplicationRecord
  has_many :users
  has_many :links, inverse_of: :shop
  has_many :shop_languages, inverse_of: :shop, dependent: :destroy
  has_many :languages, through: :shop_languages
...

即使我在声明关系之前移动回调上限:

class ShopLanguage < ApplicationRecord
  before_destroy :check_last_language, prepend: true
...

它也不起作用。这是我在rails控制台中尝试的内容:

shop = Shop.first
#<Shop id: 2, category: "Sport", fax: nil, identifier: 1, leader: "XXXXX", modified_by: "batch.update.shops", name: "ENGLOS", opening_date: "1976-07-27", phone: "0320001700", status: "open", created_at: "2018-03-22 16:39:35", updated_at: "2018-03-22 16:39:35"> 

2.4.0 :002 > lang = Language.find(245)
  Language Load (0.4ms)  SELECT  "languages".* FROM "languages" WHERE "languages"."id" = $1 LIMIT $2  [["id", 245], ["LIMIT", 1]]
 => #<Language id: 245, tag: "fr-FR", created_at: "2018-03-22 16:25:59", updated_at: "2018-03-22 16:25:59"> 

shop.languages
  Language Load (1.0ms)  SELECT  "languages".* FROM "languages" INNER JOIN "shop_languages" ON "languages"."id" = "shop_languages"."language_id" WHERE "shop_languages"."shop_id" = $1 LIMIT $2  [["shop_id", 2], ["LIMIT", 11]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Language id: 245, tag: "fr-FR", created_at: "2018-03-22 16:25:59", updated_at: "2018-03-22 16:25:59">]> 


shop.languages.delete(lang)
   (0.4ms)  SAVEPOINT active_record_1
  SQL (0.5ms)  DELETE FROM "shop_languages" WHERE "shop_languages"."shop_id" = $1 AND "shop_languages"."language_id" = 245  [["shop_id", 2]]
   (0.2ms)  RELEASE SAVEPOINT active_record_1
 => [#<Language id: 245, tag: "fr-FR", created_at: "2018-03-22 16:25:59", updated_at: "2018-03-22 16:25:59">] 
2.4.0 :007 > shop.languages.size
   (3.5ms)  SELECT COUNT(*) FROM "languages" INNER JOIN "shop_languages" ON "languages"."id" = "shop_languages"."language_id" WHERE "shop_languages"."shop_id" = $1  [["shop_id", 2]]
 => 0 

所以,回调还没有被调用。 问题在于,如https://docs.microsoft.com/en-us/sql/t-sql/functions/try-cast-transact-sql中所述,

  

5跳过回调与验证一样,也可以   使用以下方法跳过回调:

     

递减decrement_counter delete delete_all

所以,如果我做对了,当我在我的收藏中调用delete时:

shop.languages.delete(lang)

永远不会调用我的回调方法。 更多的是,Active Record或Active Model回调在返回false时不再停止,因此唯一的解决方案是使用:

throw(:abort) if shop.languages.count < 2

问题是如何正确删除表shop_languages中的记录而不删除表languages中的相应语言记录?运用 throw(:abort) if ...似乎没有足够的可读性和便捷的API响应方式。

1 个答案:

答案 0 :(得分:0)

我不确定这是否可以解决您的问题,但是您可以覆盖#destroy

def destroy
  if ...
    super
  else
    # no-op
  end
end