Rails:确保一次只有一个布尔字段设置为 true

问题描述 投票:0回答:9

我有一个模型,其中包含

name:string
default:boolean
字段。我希望
true
值是唯一的,以便一次只能将数据库中的一项设置为 true。如何在控制器中设置更新和新操作以将项目的所有其余值设置为
false

假设我的数据库中有以下设置

name:string | default:boolean |  
Item1       | true            |  
Item2       | false           |  
Item3       | false           |  

如果我将

Item2
默认值更改为 true,我希望它循环遍历所有项目并将其余项目设置为
false
,因此一次只有一个是
true
,所以看起来像这样。

name:string | default:boolean |  
Item1       | false           |  
Item2       | true            |  
Item3       | false           | 
ruby-on-rails ruby rails-activerecord
9个回答
22
投票

此代码是从以前的答案中窃取并稍微简化的:

def falsify_all_others
  Item.where('id != ?', self.id).update_all("default = 'false'")
end

您可以在模型中的 before_save 回调中使用此方法。

实际上,最好只“伪造”那些值为“true”的记录,如下所示:

Item.where('id != ? and default', self.id).update_all("default = 'false'")

更新:为了保持代码干燥,请使用

self.class
而不是
Item

self.class.where('id != ? and default', self.id).update_all("default = 'false'")

5
投票

我觉得在你伪造别人之前先检查一下你保存的是否是真的是很好的。否则,当您保存不活跃的记录时,您就会伪造所有人。

def falsify_all_others
    if self.default
        self.class.where('id != ? and default', self.id).update_all("default = 'false'")
    end
end

3
投票

在您的控制器代码中,您可以执行类似的操作...请注意,您可能将 Item2 作为参数 [...],因此您可以在下面互换它

@items_to_be_falsified = Item.where('id != ?', Item2.id)

@items_to_be_falsified.each do |item|
  item.default = false
  item.save
end

请注意,当您开始使用此功能时,最好将其移至模型中,使其成为一个函数并像下面的

Item2.falsify_all_others
那样调用它

def falsify_all_others
  Item.where('id != ?', self.id).each do |item|
    item.default = false
    item.save
  end
end

享受吧!


3
投票

我还建议伪造您的所有记录,然后使其真实。

add_column :users, :name ,:boolean, default: false

3
投票

好的,您还需要一些东西。

不要使用默认字段名称,它通常是为数据库保留的。 将记录保存为默认值 false 会将所有记录设置为 false,这不是您想要的。检查我们是否将此记录设置为 true 并设置为 false。

  before_save :falsify_all_others
  def falsify_all_others
    if is_default
      self.class.where('id != ?', self.id).where('is_default').update_all(:is_default => false)
    end
  end

2
投票

更多 ActiveRecord,更少原始 SQL 决策

after_commit :reset_default, if: :default?

private

def reset_default
  self.class.where.not(id: id).where(default: true).update_all(default: false)
end

2
投票

如果您最近来到这里并且正在使用 Rails 6,那么应该在数据库级别和模型级别上涵盖这一点:

分贝级别:

add_index :items, :default, unique: true, where: '(default IS TRUE)'

型号级别:

class Item < ApplicationRecord
  scope :default, -> { where(default: true) }
  
  validates :default, uniqueness: { conditions: -> { default } }
end

1
投票

如果您希望它用于创建和更新(rails v4) 记下 rails 指南

中的这个花絮

after_save 在创建和更新时运行,但总是在 more 之后运行 特定回调 after_create 和 after_update,无论顺序如何 其中执行了宏调用。


1
投票
class Model < ApplicationRecord
  before_save :ensure_single_default, if: :is_default?

  private

  def ensure_single_default
    self.class.update_all(is_default: false)
  end
end

您不需要检查 id,因为此回调发生在保存 true 之前。

© www.soinside.com 2019 - 2024. All rights reserved.