inimene
inimene

Reputation: 1650

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. 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.

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. 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.

Upvotes: 0

Views: 818

Answers (2)

user4776684
user4776684

Reputation:

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.

In this case you can remove Rails uniqueness: { case_sensitive: false }, on: :create because it clearly does not do what you want.

I think it would be the cleanest way that comes to my mind.

Upvotes: 1

Matouš Borák
Matouš Borák

Reputation: 15934

If you use MySQL, you could instead try INSERT ... ON DUPLICATE KEY UPDATE ... (see the docs). For this to work you would have to add a unique index for the name column though. 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 (e.g. a counter) instead of the inserted values. You can watch this issue though.

Upvotes: 1

Related Questions