Phong Tình
Phong Tình

Reputation: 21

Rails has_many through condition context

I have three model Role, Action and RoleAction with some code:

class Role < ActiveRecord::Base
    has_many :users
    has_many :actions, -> {where role_actions:{status: 1}}, :through => :roleActions
    has_many :actions, :through => :roleActions #other context(manager, etc...)
    has_many :roleActions
end

class Action < ActiveRecord::Base
    has_many :actions, foreign_key: 'parent_id'
    has_many :roleActions
    has_many :roles, through: :roleActions
end

class RoleAction < ActiveRecord::Base
    belongs_to :role
    belongs_to :action
end

When I using role.actions will get actions of role and have status == 1 in role_actions.

But I want to when I using role.actions("manager") (with "manager" is context name) will return all action of role.

How can I do?

Thanks!

Upvotes: 2

Views: 288

Answers (1)

Richard Peck
Richard Peck

Reputation: 76774

  1. You need to keep your associations snake_case
  2. You cannot have multiple associations with the same name (IE actions)

Here's what I'd do:

#app/models/role.rb
class Role < ActiveRecord::Base
   has_many :role_actions
   has_many :actions, through: :role_actions do
      def status(val)
        { where status: val } # @role.actions.status(1)
      end
   end
end

#app/models/role_action.rb
class RoleAction < ActiveRecord::Base
   belongs_to :role
   bleongs_to :action
end

#app/models/action.rb
class Action < ActiveRecord::Base
   has_many :role_actions
   has_many :actions, through: :role_actions
end

You'll want to look up scopes in Rails - it's bad practice to define conditions in your association query, only to then want to break it. Some would call it an antipattern.

--

If you have the "naked" assocation, you'll be able to scope that however you want. You can also use ActiveRecord association extensions to provide specific functionality on your association itself (as demonstrated above).

role.actions("manager")

This can be achieved by simply invoking the Role object when looking up the manager value:

@role = Role.find_by name: "manager"
@role.actions #-> all actions for the "manager" role.

Upvotes: 1

Related Questions