Rails 3 default scope, scope with override
Asked Answered
A

2

8

I have a situation where the behavior of an existing app is changing and it's causing me a major headache.

My app has Photos. Photos have a status: "batch", "queue", or "complete". All the existing Photos in the app are "complete".

99% of the time I need to only show complete photos, and in all of the existing codebase I need every call to Photos to be limited to only complete photos.

However, in the screens related to uploading and classifying photos I need to be able to fairly easily override that default scope to show batched or queued photos.

Like many others, I need to find a way to easily override the default scope in certain situations. I looked at these questions (1, 2) and they don't seem to answer what I'm looking for.

The code I wish worked is this:

class Photo < ActiveRecord::Base
  ...
  default_scope where(:status=>'complete')
  scope :batch, unscoped.where(:status=>'batch')
  scope :queue, unscoped.where(:status=>'queue')
  ...
end

However, that doesn't work. I tried wrapping the scope methods in lambdas, and that didn't work either.

I realize default_scope comes with baggage, but if I can't use it with overrides then I'm looking at adding scope :complete ... and having to comb through every call to photos in my existing app and add .complete in order to filter unprocessed photos.

How would you solve this problem?

Astigmatism answered 2/8, 2011 at 22:9 Comment(5)
Your exact code works fine for me on Rails 3.0.9. Can you use the console to run Photo.scoped.to_sql and Photo.batch.to_sql and post the output?Leonoreleonsis
@dylan - I'm also in 3.0.9 and the batch scope gives "where (status = 'complete') and (status = 'batch')Messmate
@Messmate :( yeah, I think I'm realizing that's the only way. ****Astigmatism
@Messmate that is so weird... I copy and pasted his code into a fresh app and didn't get the conflicting status = ... parts...Leonoreleonsis
@dylan - double-checked and I'm 3.0.7, maybe they changed the behavior?Messmate
M
6

def self.batch
  Photo.unscoped.where(:status=>"batch")
end
This thread is more authoritative: Overriding a Rails default_scope
Messmate answered 2/8, 2011 at 22:20 Comment(6)
I thought of this, and I tried it, but then I end up with this problem: user.photos.batch returns all photos (because the unscoped call on the model breaks the association).Astigmatism
you're semi-screwed here. You could add a User::photos method that returns the unscoped photos for the user, but otherwise default_scope is kind of nasty.Messmate
Out of curiosity, do you know what it would take to override the default_scope method to change the behavior? I'm interested in taking a stab at that, but not sure where to begin.Astigmatism
here's the rdoc, click 'show source' to see how default_scope works: api.rubyonrails.org/classes/ActiveRecord/…Messmate
Great, thanks! I'll leave this question open a bit longer in case anyone else has any thoughts, but if no one does I'll accept your answer as it does kind of work.Astigmatism
also - here's the source for ActiveRecord::Base - github.com/rails/rails/blob/master/activerecord/lib/…Messmate
N
4

I give it a shot. Lets say you want to remove a where clause from a default scope (and not just override it with another value) and keep associations you can try this:

class Photo < ActiveRecord::Base
  default_scope where(:status => 'complete').where(:deleted_at => '').order('id desc')

  def self.without_default_status
    # Get the ActiveRecord::Relation with the default_scope applied.
    photos = scoped.with_default_scope
    # Find the where clause that matches the where clause we want to remove 
    # from the default scope and delete it.
    photos.where_values.delete_if { |query| query.to_sql == "\"photos\".\"status\" = 'complete'" }
    photos
  end

end
Nagana answered 20/3, 2013 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.