【问题标题】:Unique constraint issue paranoia gem独特的约束问题偏执宝石
【发布时间】:2016-09-05 14:15:47
【问题描述】:

我有一个 Rails 应用程序,我在其中使用 devise 和 paranoia gems。
我在 postgres db 中有一个 users 表,它在 email 列上有唯一的验证。 我正在使用偏执狂进行软删除,问题是当我删除用户然后使用已删除用户的电子邮件创建用户时,它会引发错误 PG::UniqueViolation: ERROR
我已经阅读了这一点,并且知道这可以使用 rails 的部分索引功能来解决。

http://scottsmerchek.com/2015/08/03/taking-the-d-out-of-crud/

https://devcenter.heroku.com/articles/postgresql-indexes#partial-indexes

我该如何实现?
抱歉格式错误,从手机输入。

【问题讨论】:

    标签: ruby-on-rails postgresql activerecord devise ruby-paranoia


    【解决方案1】:

    由于您将用户删除为软删除,因此电子邮件不会从数据库中删除,因此仅将用户属性 is_deleted 设置为 true。

    现在要解析PG::UniqueViolation: ERROR,您必须在字段电子邮件和deleted_at 上创建唯一索引 所以你的迁移将是

    class AddUniqueIndexToUsers < ActiveRecord::Migration
      def change
        remove_index :users, column: :email
        add_index :users, [:email, :deleted_at], unique: true
      end
    end
    

    【讨论】:

    • 我正在使用支持部分索引的 postgres,CREATE UNIQUE INDEX customer_name_index ON customer(name) WHERE removed_on IS NULL;。我怎么得到这样的东西?什么是迁移? @Rakesh
    • 您尝试过上述解决方案吗?
    • 你能解释一下为什么你在 add_index 之前做了 remove_index 吗?是否默认添加索引? @RakeshPatidar
    • 我使用以下迁移解决了这个问题:class AddPartialIndexToUsers &lt; ActiveRecord::Migration def change remove_index :users, column: :email add_index :users, :email, unique: true, where: "deleted_at = NULL" add_index :users, :phoneno, unique: true, where: "deleted_at = NULL" end end 这对我来说似乎更好,因为在您的迁移中我并不特别需要 deleted_at 是唯一的......这似乎是我需要的更多...如果我在某处有错误,请提及它,或更新您的 asnwer,以便我可以接受它@Rakesh
    • 我相信上面的解决方案行不通。见this doc。因为 NULL != NULL,您可以再次插入 ("a@b.com", NULL) 和 ("a@b.com", NULL) 并且约束将允许它。我认为 SiD Devesh 的回答是正确的。
    【解决方案2】:

    Rakesh 的答案是错误的,并且将允许在数据库中重复,因为当其中一个值为 NULL 时,复合 UNIQUE 索引将失败(并且 deleted_at 默认为 NULL)。

    要完成这项工作,您实际上需要以相反的方式将这种细微差别与唯一索引一起使用,您可以有一个整数列,如 acive:tinyint (default 1) 和一个复合索引,如下所示:

    add_index :users, [:email, :active], unique: true
    

    创建新用户时不需要特殊操作,因为active 列的默认值为1。但每次删除记录时,除了设置deleted_at 外,还需要将active 设置为NULL。这样一来,您就可以只拥有一条活动记录,并使用相同的电子邮件地址删除许多记录。

    在 PostgreSQL 或 MySQL > 8.0.13 中,您可以在没有此附加列的情况下工作,并使用如下功能索引:

    CREATE UNIQUE INDEX unique_email
                     ON users (email, (IF(deleted_at IS NULL, 1, NULL)));
    

    结果将类似于带有active 列的示例。删除记录后,唯一索引将停止为此记录工作,从而允许您拥有多个具有相同 email 的已删除记录。

    【讨论】:

      【解决方案3】:

      它会在 Postgresql 上失败,可能的解决方案是

      class AddUniqueIndexToUsers < ActiveRecord::Migration
        def change
          remove_index :users, column: :email
          add_index :users, [:email, :deleted_at], unique: true, where: "deleted_at is null"
        end
      end
      

      参考https://www.ironin.it/blog/partial-unique-indexes-in-postgresql-and-rails.html

      【讨论】:

        【解决方案4】:

        足够了:

        class AddUniqueIndexToUsers < ActiveRecord::Migration
          def change
            remove_index :users, column: :email
            add_index :users, :email, unique: true, where: 'deleted_at IS NULL'
          end
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-03-23
          • 2013-01-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多