Say an admin can be the owner of an account and an account can have many admins. You would have association like the followings :
class Account < ApplicationRecord
belongs_to :account_owner, foreign_key: :account_owner_id, class_name: 'Administrator', optional: true
has_many :administrators
end
class Administrator < ApplicationRecord
# account instance has an account_owner_id that references the administrator "app owner"
has_one :account
# administrator has an account_id on it that references the account he belongs_to
belongs_to :account
That code doesnt work properly because the instance method account defined twice on Administrator clash with each other.
Then I would tend to writte something like this :
class Administrator < ApplicationRecord
has_one :account, as: :proprietary_account
belongs_to :account
to differentiate between the two "sides" of the association.
However this doesnt work either and method proprietary_account is not created
Is there a way to model that with rails associations ? If not how would you create this kind of relationship in a rails app.
You need to create another table called admin_accounts and use the has_many :through association. The admin_accounts table will have two columns, one with admin_id, and one with account_id. You can then associate the two models through that table. More information here: https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Related
I want to make an association where a user has many deals and a deal belongs to a user as well as another with a role ('associate').
I am using the rolify gem to do this.
Like this:
# user.rb
has_many :deals
# deal.rb
belongs_to :user
belongs_to :associate # User with role 'associate or admin'
The first belongs to could be whatever user, doesn't matter if the user is any role it should still work, the second belongs to however should most definitely be an associate or an admin
Do you think I should use rolify for this? or should I not and just make a different model for each role?
Update
My current solution won't work because the user as an associate would need two associations, the has many deals and the has_many :client_deals. I'm not sure in the naming still.
Update 2
Max's solution works great!
This is not where you want to use Rolify's tables. Rolify creates a one-to-one assocation between roles and resources through the roles tables. Roles then have a many-to-many assocation through the users_roles table to users.
Which means it works great for cases where the association is one-to-many or many-to-many but Rolify really can't guarantee that there will ever only be one user with a particular role due to the lack of database constraints.
Even if you add validations or other application level constraints that still leaves the potential for race conditions that could be a double click away.
Instead you want to just create separate one-to-one assocations:
class Deal < ApplicationRecord
belongs_to :creator,
class_name: 'User',
inverse_of: :created_deals
belongs_to :associate,
class_name: 'User',
inverse_of: :deals_as_associate
validates :must_have_associate_role!
private
def must_have_associate_role!
# this could just as well be two separate roles...
errors.add(:associate, '^ user must be an associate!') unless associate.has_role?(:associate)
end
end
class User < ApplicationRecord
has_many :created_deals,
class_name: 'Deal'
foreign_key: :creator_id,
inverse_of: :creator
has_many :deals_as_associate,
class_name: 'Deal'
foreign_key: :associate_id,
inverse_of: :associate
end
Two models can really have an unlimited number of associations between them as long as the name of each assocation is unique and you configure it correctly with the class_name and foreign_key options.
Since this uses a single foreign key this means that ere can ever only be one and you're safeguarded against race conditions.
For associate you can use the following
belongs_to :associate, -> { includes(:roles).where(roles: {name: ['associate', 'admin'] }) }, class_name: 'User', foreign_key: 'user_id'
I have two models User and UserRelation. The case is that User has several related users with himself(recommended by him), but he has only one person related_to(person who recommended him).
I would like to return from User object collection of recommended users and user who recommended him. I have written association for returning users collection and it works but I have no idea how should I write has_one association.
I get this error:
ActiveRecord::HasOneThroughCantAssociateThroughCollection: Cannot have a has_one :through association 'User#relation' where the :through association 'User#user_relations' is a collection. Specify a has_one or belongs_to association in the :through option instead
User model:
class User < ActiveRecord::Base
has_many :user_relations
has_many :related_users, through: :user_relations, source: :related_user
has_one :relation, through: :user_relations, source: :user
end
UserRelation model:
class UserRelation < ActiveRecord::Base
belongs_to :user
belongs_to :related_user, class_name: 'User'
end
UserRelation columns:
user_id
related_user_id
My choice would be to put a foreign key in your User table for the possible related_to field.
If the requirement is that it can only be one (or none) then why not?
You still keep the other "user_relations" for all other types. All the time in rails, we map to the same entity in different ways. It's not uncommon at all
I am currently building a registration system which will need to create both a user and an organization. The Organization needs to have an Owner or System Admin. I believe my second option is best as I can create the Organization, a Sys Admin Role and a User without any circular database issues. My fear with the First option is that an Organization is owned by a user and that user also belongs to that organization.
First --
Class User
belongs_to :organization
belongs_to :role
has_one :organization_as_owner, class_name: "Organization", foreign_key: "owner_id"
Class Organization
belongs_to :owner, class_name: "User"
has_many :users
has_many :roles
Class Role
belongs_to :organization
has_many :users
Second --
Class User
belongs_to :organization
belongs_to :role
Class Organization
has_many :users
has_many :roles
Class Role
belongs_to: organization
has_many :users
Using the Second option, I would utilize a permissions table for the Sys Admin, and permit them to add new Roles, etc.. Which setup would you prefer and why? Would using a has_many through on either Option be beneficial?
I would probably set up the relationships like this:
Class User
belongs_to :organization
belongs_to :role
Class Organization
has_many :users
has_many :roles
Class Role
has_many :users
belongs_to: organization
This is assuming all users have only one role. This way, you can make the following calls:
user.role
user.organization
organization.users
organization.roles (this will just collectively list all the roles that the users inside the org have, can always run a uniq! on this if necessary
role.user
role.organization
However, I think you should also probably take a look at the excellent rolify gem that could probably help you do this out of the box: https://github.com/EppO/rolify
I've got a rails application built for schools. The users in the system are parents who track their childrens’ activities (aka family members or just members). Teachers are also users and can log in to participate in activities. The Member model is the place that the application uses to find participants. So that teachers can be found they are added to the Member model and for the same reason, parents are added as well.
A teacher added to this application would have one record in each table user, user_member, and member. A record in user to allow him to login, 1 record in member so he can be searchable, and 1 in user_member to make the association. In this case finding a user’s own member record is trivial. But if I were a parent with a login and 2 children in the system, James and Brian, one would find 3 records in the Members table, one for James, one for Brian and one for myself. I need to know which member record is mine, not James or Brian.
What’s the best way to model this? I’ve considered two options 1) there’s a foreign key in the user table that points to a user’s own member_id OR 2) the user_members table has a boolean called ‘own_member’ which indicates this user_id’s member_id is the member record for the user. Is there one that would be preferable? One more rails like? And how would I make the call build/create the associations?
The current relationships are modeled like this:
class User < ActiveRecord::Base
has_many :user_members
has_many :members, :through => :user_members
end
class UserMember < ActiveRecord::Base
belongs_to :user
belongs_to :member
end
class Member < ActiveRecord::Base
has_many :user_members
has_many :user, :through => :user_members
end
It sounds like the UserMember join model describes which members a user has privileges to access. This is a different concept than tracking which Member record is associated to a particular user. It sounds like a user would have a member record, and a member would belong to a user implying the following database structure:
class User < ActiveRecord::Base
has_many :user_members
has_many :members, :through => :user_members
has_one :member
end
class UserMember < ActiveRecord::Base
belongs_to :user
belongs_to :member
end
class Member < ActiveRecord::Base
has_many :user_members
has_many :user, :through => :user_members
belongs_to :user
end
This means adding another database column to the members table for "user_id". Creating and building the member record from a User instance could be done as follows:
user = User.new
user.build_member
user.create_member
More info on building through associations here: http://ar.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
You might consider creating callbacks to automatically create a member record on creation of a user record so you can skip manually building the associated record. An example would be:
# app/models/user.rb
class User < ActiveRecord::Base
before_create :build_member
def build_member
self.build_member
end
end
More information on callbacks here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Often three (or more) way associations are needed for habtm associations. For instance a permission model with roles.
A particular area of functionality has many users which can access many areas.
The permissions on the area are setup via roles (habtm)
The user/roles association is also habtm
The permissions (read, write, delete, etc) are habtm towards roles.
How would that be best done with rails/activerecord?
I'm not sure if you are just using Role based user permissions as an example, or if that is actually your goal.
Nested habtm relationships are easy in Rails, though I would highly recommend Nested has_many :through, just set them up as you would imagine:
class Permission < ActiveRecord::Base
end
#this table must have permission_id and role_id
class PermissionAssignment < ActiveRecord::Base
belongs_to :permission
belongs_to :role
end
class Role < ActiveRecord::Base
has_many :users, :through => :role_assignments
has_many :permissions, :through => :permission_assignments
end
#this table must have user_id and role_id
class RoleAssignment < ActiveRecord::Base
belongs_to :role
belongs_to :user
end
class User < ActiveRecord::Base
has_many :roles, :through => :role_assignments
end
This question about rails and RBAC (role-based authentication control) has a bunch of useful samples that provide the answer to this question and also sample implementations of your example.