简体   繁体   English

验证失败时如何不回滚ActiveRecord事务

[英]How to not rollback a ActiveRecord transaction in case of failed validation

So I have a Model on my Rails app that has a name field in it. 所以我在Rails应用程序上有一个模型,其中有一个名称字段。 In the database this field has a unique constraint set against it. 在数据库中,此字段具有针对它的唯一约束集。 In the model level I am also doing a uniqueness: { case_sensitive: false }, on: :create check. 在模型级别,我还在做uniqueness: { case_sensitive: false }, on: :create检查。

My idea is that, after the ActiveRecord validations are done and if the uniqueness check failed then I want to increment a counter for the all ready existing record in the same table. 我的想法是,在完成ActiveRecord验证之后,如果唯一性检查失败,那么我想为同一表中所有准备就绪的现有记录增加一个计数器。 So what I do right now is this: 所以我现在要做的是:

after_validation :term_exists

    private
    def term_exists
        if self.errors.added? :name, :taken
            Term.where('LOWER(name) = LOWER(?)', self[:name]).update_all('popularity_counter = popularity_counter + 1')
        end
    end

This would be great but when I run my tests against it, the counter never gets incremented. 这将很棒,但是当我对其进行测试时,计数器永远不会增加。 I imagine that the reason for this is that everything in that transaction will be rolled back once it turns out the validations failed. 我想这是因为一旦验证失败,该事务中的所有内容都会回滚。

But what would be the rails way to solve this kind of problem then? 但是,解决这种问题的正确方法是什么? It is strongly advised not to keep such logic in controllers. 强烈建议不要在控制器中保留此类逻辑。

If you use MySQL, you could instead try INSERT ... ON DUPLICATE KEY UPDATE ... (see the docs ). 如果使用MySQL,则可以改用INSERT ... ON DUPLICATE KEY UPDATE ... (请参阅docs )。 For this to work you would have to add a unique index for the name column though. 为此,您必须为name列添加唯一索引。 You would not need any uniqueness validation in this scenario. 在这种情况下,您不需要任何唯一性验证。

There is also a promising gem called upsert (which is a commonly used acronym for "update instead of insert") but it does not yet support updating a different column (eg a counter) instead of the inserted values. 还有一个很有前途的宝石,称为upsert (这是“更新而不是插入”的常用缩写),但它尚不支持更新其他列(例如,计数器)而不是更新插入的值。 You can watch this issue though. 不过,您可以观看此问题

I would say that you need to create your custom before_validation method where you can check whether the name is unique and if not then update your counter and then fail validation. 我要说的是,您需要创建自定义的before_validation方法,可以在其中检查名称是否唯一,如果不是,则更新计数器,然后使验证失败。

In this case you can remove Rails uniqueness: { case_sensitive: false }, on: :create because it clearly does not do what you want. 在这种情况下,您可以删除Rails的uniqueness: { case_sensitive: false }, on: :create因为它显然不能满足您的要求。

I think it would be the cleanest way that comes to my mind. 我认为这是我想到的最干净的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM