I need to do some filtering on my ActiveRecord models, I want to filter all my model objects by owner_id. The thing I need is basically the default_scope for ActiveRecord.
But I need to filter by a session variable, which is not accessible from the model. I've read some solutions, but none works, basically any of them says that you can use session when declaring default_scope.
This is my declaration for the scope:
class MyModel < ActiveRecord::Base
default_scope { where(:owner_id => session[:user_id]) }
...
end
Simple, right?. But it fails saying that method session does not exists.
Hope you can help
Session objects in the Model are considered bad practice, instead you should add a class attribute to the User
class, which you set in an around_filter
in your ApplicationController
, based on the current_user
class User < ActiveRecord::Base
#same as below, but not thread safe
cattr_accessible :current_id
#OR
#this is thread safe
def self.current_id=(id)
Thread.current[:client_id] = id
end
def self.current_id
Thread.current[:client_id]
end
end
and in your ApplicationController
do:
class ApplicationController < ActionController::Base
around_filter :scope_current_user
def :scope_current_user
User.current_id = current_user.id
yield
ensure
#avoids issues when an exception is raised, to clear the current_id
User.current_id = nil
end
end
And now in your MyModel
you can do the following:
default_scope where( owner_id: User.current_id ) #notice you access the current_id as a class attribute
You will not be able to incorporate this into a default_scope. This would break every usage within (e.g.) the console as there is no session.
What you could do: Add a method do your ApplicationController like this
class ApplicationController
...
def my_models
Model.where(:owner_id => session[:user_id])
end
...
# Optional, for usage within your views:
helper_method :my_models
end
This method will return a scope anyhow.
Session related filtering is a UI task, so it has its place in the controller. (The model classes do not have access to the request cycle, session, cookies, etc).
What you want is
# my_model_controller.rb
before_filter :retrieve_owner_my_models, only => [:index] # action names which need this filtered retrieval
def retrieve_owner_my_models
@my_models ||= MyModel.where(:owner_id => session[:user_id])
end
Since filtering by ownership of current user is a typical scenario, maybe you could consider using standard solutions, like search 'cancan gem, accessible_by'
Also be aware of the evils of default_scope. rails3 default_scope, and default column value in migration