Rails has_many_through and multiple items - ruby-on-rails

I have many models using a has_many_through relationship: users, roles, and security_items.
A user can be in multiple roles, a role can have many security items. So if a user is in multiple roles, how can I find out if a particular item is any of the roles? Like if its true in one but false in the other, the true should take precedence.
If a user is only in one role then the following works, but if a user is multiple roles then the following fails in rails console
role = RoleMembership.where("user_id = ?", user.id)
role.security_items.exists?(1)
Error if the user is in multiple roles:
NoMethodError: undefined method `security_items' for #<ActiveRecord::Relation::ActiveRecord_Relation_RoleMembership:0x00000102da5e28>`
How can I check each and every role to see if the item exists in the table?

Try role.map(&:security_items).flatten.uniq to get all the security items.

RoleMembership.where("user_id = ?", user.id).select { |role| role.security_items.present? }
Working upon the code that you put up, thats how I would do it.
EDIT:
If you get the NOMETHOD error, it means there isn't a method called security_items for the role. Is your relationship set up right in the models? You can try in the console
RoleMembership.find(1).security_items
If that errors out then you have identified your problem.

Related

Rails using 'where' on a model that has a has_many relationship

I have 2 models, Account and User
class Account
has_many :users
end
class User
belongs_to :account
end
The relevant model details for my issue are:
Every Account has a subscription type, which can be either standard, premium or enterprise
How do I properly list all Users that belong to a premium Account? Additionally, how do I list all Users in all premium Accounts, since there can be any number of premium Accounts.
I've tried several variations of the things below:
Account.where(subscription: "premium").users # undefined method users
User.where(subscription: "premium") # returns nothing
Account.where(subscription: "premium").each do |account|
account.users # seems to return an array?
end
What you want is a left inner join:
User.joins(:account)
.where(accounts: { subscription: "premium" })
This will return any records from the users table that have a match in the joined table.
How do I properly list all Users that belong to a premium Account?
If what you mean is users that belong to a specific account you would call the #users method on that specific account.
account = Account.eager_load(:users).find(1)
account.users
For your first question:
list all Users that belong to a premium Account:
Your first try (Account.where(subscription: "premium").users) doesn't work because Account.where(subscription: "premium") already returns many Accounts, so a list (more precisely an ActiveRecord Relation object) and you can't call .users on that list. You can only call .users on one account
What you need is a join statement to join both tables. You can read more about it here: https://guides.rubyonrails.org/active_record_querying.html#joining-tables
And since you want users, you should start with user.
Your query should look something like that:
User.joins(:account).where(accounts: {subscription: "premium"})
This will give you all Users that have an account with the subscirption type premium.
I think your second question regarding the query is acutally the same
Additionally, how do I list all Users in all premium Accounts, since there can be any number of premium Accounts.
The other answers are great, another option would be in Account model
scope :premium, -> { where(subscription: 'premium') }
and your query
User.joins(:account).merge(Account.premium)

Rolify Assign Multiple User Roles at Once

For the Rolify gem in Rails in order to add a role to a user you can do:
user = User.find(1)
user.add_role :admin
But I have a large database of already existing users with no roles. How do I add a role to multiple users with a single command?
I tried the snippet below but it errored:
users = User.where(email:['email1','email2'])
users.addrole :admin
Does anyone know how to do this? Or do I need to create a script that cycles through the users automatically and assigns a role one by one?
Rolify is mostly just a quick and dirty role system, and it currently doesn't provide this feature.
You'll have to write a bulk insert/upsert query of your own into the database tables that Rolify persists data, e.g. (roles and users_roles for a users table).

Active Record Where Clause For Relation In Model

I have two Models User and Site. The User has category like 'basic and premium'. The user has relation to sites one -> many (i.e one user can have more than one sites). Now i want to select sites of premium user. Can someone tell me how to use where clause in ActiveRecord to achieve this?
I want to select sites of premium user
This will do
User.includes(:sites).where('users.category = ?', 'premium')
Update
If sites also have categories like 'wordpress or joomla', how do i
apply where clause to select only wordpress sites of premium users
For that you need to tweak the query like this
User.includes(:sites).where('users.category = ? and sites.category = ?', 'premium','wordpress')
As you said that category is a column, so Rails automatically generates the following method for you:
User.find_by_category("basic")
If you do not want to use this, you can use where method, in which you have to send a key-value pair like following:
User.where(:category => "basic")
And when you have found a user with the category you desired, you can simply call sites on it to get all the associated sites with a particular user.
You can try this also:
#user_ids = User.where(category: 'premium').pluck(:id)
#sites = Site.where(user_id: #user_ids)

Gem, update and compatibility

I have a User model, the user can gradually insert information on their profile (age, description, avatar, etc..). Those users can be viewed in the public web site only if they have complete their entire profile.
Whats is the best way in rails to put constraint on query without polluting every single call to Active Record User model. Is there're a way for
User.all
to return result with those constraints by default?
Tks a lot!
You could define a scope.
# user.rb
scope :complete, where("age IS NOT NULL", "description IS NOT NULL",...)
Then you can just do User.complete and it will fetch User objects matching those conditions. For more information:
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html

Ruby on Rails updating join table records

I have two models Users and Roles. I have setup a many to many relationship between the two models and I have a joint table called roles_users.
I have a form on a page with a list of roles which the user checks a checkbox and it posts to the controller which then updates the roles_users table.
At the moment in my update method I am doing this because I am not sure of a better way:
role_ids = params[:role_ids]
user.roles.clear
role_ids.each do |role|
user.roles << Role.find(role)
end unless role_ids.nil?
So I am clearing all the entries out then looping threw all the role ids sent from the form via post, I also noticed that if all the checkboxes are checked and the form posted it keeps adding duplicate records, could anyone give some advice on a more efficent way of doing this?
You can do a direct assignment, as that handles the dirty work for you:
user.roles = params[:role_ids].present? ? Role.find_all_by_id(params[:role_ids]) : [ ]
ActiveRecord should take care of creating new associations or removing those that are no longer listed. If anything precludes your join model from saving, such as a failed validation, you may have issues, but in most situations this should work as expected.
I hope you're using a has_many ..., :through for this one and not the deprecated has_and_belongs_to_many that keeps haunting so many Rails apps because of old example code.

Resources