【问题标题】:Rails: Ensure only one boolean field is set to true at a timeRails:确保一次只有一个布尔字段设置为 true
【发布时间】:2012-04-13 03:36:13
【问题描述】:

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

假设我有以下设置 在我的数据库中
模特标志
名称:字符串 |默认值:布尔值 |
项目1 |真的 |
项目2 |假 |
项目3 |假 |

如果我将 Item2 的默认值更改为 true,我希望它遍历所有徽标并将其余徽标设置为 false,因此一次只有一个为 true,所以看起来像这样。

名称:字符串 |默认值:布尔值 |
项目1 |假 |
项目2 |真的 |
项目3 |假 |

提前感谢您的帮助。

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    此代码是从之前的答案中窃取的,并略有简化:

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

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

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

    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'")
    

    【讨论】:

    • 最好使用标准 sql 进行 id 比较 'id <> ?'。确保仅在默认值的当前值也为 true 时才触发 update_all。
    • 我使用 after_save 以防保存失败。
    • @MarianoCavallo apidock.com/rails/v4.0.2/ActiveRecord/Relation/update_all - 简而言之,update_all 不会实例化相关模型,也不会触发 Active Record 回调或验证。
    【解决方案2】:

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

    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
      

      享受吧!

      【讨论】:

      • 这非常有效,谢谢,现在我已经学会了如何在未来做这样的事情。
      • 你最好在这里使用update_all。此代码可能会执行数千次查询。
      【解决方案4】:

      我还建议您伪造所有记录,然后将其变为真实。

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

      【讨论】:

        【解决方案5】:

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

        不要使用默认的字段名,它通常是为数据库保留的。 将默认记录保存为 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
        

        【讨论】:

        • 我遇到了这个。我一直想知道为什么我的 falsify_all 重写了新的默认值。你是说使用列名默认会导致这样的问题?
        【解决方案6】:

        更多的 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
        

        【讨论】:

        • 我对此感到失望,因为它发生在_commit 之后,所以你知道一切都已经解决了。
        【解决方案7】:

        如果您是最近才来到这里并且使用的是 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
        

        【讨论】:

          【解决方案8】:

          如果您希望它用于创建和更新 (rails v4) 记下来自rails guides的这个花絮

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

          【讨论】:

            【解决方案9】:
            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,因为这个回调发生在真实的保存之前。

            【讨论】:

            • 我觉得这可能效果不佳,因为它会在保存之前更新,并且如果保存未完成,则不会回滚对其他记录的更改。它确实在验证后发生,所以它可能没问题。
            • 我有这个在野外工作/涵盖了规格
            猜你喜欢
            • 1970-01-01
            • 2021-10-28
            • 2020-10-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多