Owner-filtered model objects on Rails 3
Asked Answered
I

3

1

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

Interrupter answered 21/5, 2012 at 7:9 Comment(2)
Is there a reason you don't want an owner object with associations?Adventure
Hmmmm.... good point, I'll test it tomorrow, I'm out of time now. Thanks for the responseSurroundings
J
3

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
Jacinto answered 7/3, 2013 at 19:45 Comment(2)
@RubénT.F. awesome, hope you find it usefulJacinto
Thread refers to the current thread. Check here for its safety: #7896798 and client_id is an attribute of the User model, which is used in the default_scopeJacinto
V
0

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.

Vashti answered 21/5, 2012 at 9:35 Comment(0)
M
0

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

Minnesota answered 21/5, 2012 at 9:40 Comment(2)
IMHO, this is not a substitute for default_scope: when creating a new object, it doesn't have the owner_id established, when doing complex searches it doesn't filter automatically by owner_id... The default_scope is a lot more automatic.Surroundings
1. Business logic in MVC design is separated from UI, i.e., models can be used in UI-s that have no session. So such assigment of ownership to logged in users is a controller responsibility. 2) automatic owner filtering by default_scope is bad design: e.g., admin interface retrieving products for all owners or parts of your code when you actually manipulate MyModel-s. 3. default_scope is best used to set natural orders and default states (:active => true) but even these are best done in controller, initializer and db schema. This makes rails default_scope controversial in my view.Jenicejeniece

© 2022 - 2024 — McMap. All rights reserved.