Rails - Is there any way to set an overrideable "default" filter (i.e. pre-run class method) for a model's ActiveRecord::Relation queries?

StackOverflow https://stackoverflow.com/questions/19749251

Question

I have a group of records I'm pushing into a database from another format. According to my records' supposedly unique ids, some of the records I've push into my database are getting rolled back because it's saying they're duplicates. But I checked it out and although they are very similar, there are differences in the records, indicating some kind of error on either my fault while handling the data before putting it into the database or this particular state government's fault in maintaining a database with duplicate records for the same entity. I'm not sure right now.

I want to store the so called "duplicates" from this official record/list in my database but flag them with a boolean column, a column that could then be used to (based on a nil/true differentiation) filter out all of my ActiveRecord queries for that model by default.

Ideally, this would allow me to do something that would cause behavior like this:

ModelName.all.count
#=> 500

ModelName.count
#=> 623

ModelName.include_alleged_duplicates.count
#=> 623

Is there any way I could do this without breaking things too badly?

Was it helpful?

Solution

It sounds like what you're looking for is default_scope, documented here in the Rails API.

So, your model change would be something like:

class ModelName
    default_scope where(:duplicate => false)

    ...

    def self.include_alleged_duplicates
        unscoped
    end
end

unscoped does just that, it runs with zero scopes on the model. See the documentation for unscoped.

The only gotcha with default_scope is it is used on every relation in the model. Just like in your example:

ModelName.all with a default_scope will perform ModelName.where(:duplicate => false).all

If you find yourself using unscoped more and more, you might want to consider reversing your logic to make the duplicate records the default_scope and unique records the unscoped.

Hope this helps!

OTHER TIPS

Starting from Rails v5.2.3, default_scope now takes only block.

class Article < ActiveRecord::Base
  default_scope { where(published: true) }
end

Article.all # => SELECT * FROM articles WHERE published = true
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top