In my Rails app, I have the following models:
class User < ActiveRecord::Base
# user declaration
has_many :account
end
class Account < ActiveRecord::Base
belongs_to :user
end
I need to add another relation where each Account can be managed by multiple AccountManagers. However, AccountManagers themselves are also Users of this system (i.e. a User can both have an Account and manage another user's account).
I'm fairly new to Rails, and I know that I can just create another model called AccountManagers. I have a feeling that we don't NEED to make another model however; all the information contained within my proposed AccountManagers model is found in the Users model as well.
I've tried to add the following relationship to the Account model:
has_many :account_managers, through: :users, source: :users
where the Account has many managers, and each manager is declared from the User model. This doesn't work as the AccountManagers table doesn't exist (and the error in the view states that as well).
Is there a way to get this relationship to work?
You have to add another join table, say account_managements with columns and user_id and account_id
class User < ActiveRecord::Base
# user declaration
has_many :accounts
has_many :managed_accounts, :source => :account, :through => :account_managements
end
class Account < ActiveRecord::Base
belongs_to :user
has_many :account_managers, :source => :user, :through => :account_managements
end
and
class AccountManagement < ActiveRecord::Base
belongs_to :user
belongs_to :account
end
You don't need to add another class AccountManager, You can add one more relation like this:
class User < ActiveRecord::Base
# user declaration
has_many :account
has_many :managed_accounts, :class_name => 'Account', :primary_key => :account_manager_id
end
class Account < ActiveRecord::Base
belongs_to :user
belongs_to :account_managers, : class_name => :User, :foreign_key => :account_manager_id
end
For this you will need to have account_manager_id in the accounts table.
The main issue I can see here is that a user has_many accounts, but in ur example, an account only has 1 user. Are u planning to have accounts with only 1 person in them?
In relational tables there are 2 main kinds of table designs.
A. One to Many
U have a relationship where one thing has many of another thing, for instance: a Owner has many Cats. In this type of relationship the Cat table can point to the Owner table. Clearly the Owner cannot point to its cats because u would need to have a pointer for each cat. it would be something like
Owners table
cat_1
cat_2
cat_3
thats not convenient at all, since there is a maximum number of cats.
If on the other hand u have
cats table
owner_id
then each owner can have an unlimited amount of cats.
More complex
After some time u get a request, some owners share a cat. u think about it a while and decide a cat can only belong to a max of 2 owners.
so u change the cat table.
cats table
owner_1
owner_2
B. Many to Many
If you want to be able to allow a cat to have any unlimited number of owners, or a owner to have an unlimited number of cats, u will need a third table. This table keeps a link from 1 cat to 1 owner. Each row defines this relationship. This table can be named either using the names of the other 2 tables: cat_owners or since this table defines a relationship u can give it a name that defines this relationship.
In this case the relationship is Ownership, people own cats. Its hard to say how the cats would define the relationship :)
This brings me to answer ur question finally. If u have a table that defines the link between a user and an account, I like the name Membership
Membership
user_id
account_id
but as u just mentioned there are some users in an account who are Managers, u can add this flag to the membership table.
Membership
user_id
account_id
manager (true/false)
u could take it a step beyond that and change manager, to a role column, and then every user can have a different role in every account.
What if each user can have multiple roles in each account? Then u will need more tables
Rails doesnt mean u can skip understanding relational databases structure.
Related
So I want my User model to have_many Skills. I want there to be two different categories for the skills: a wanted skill, and a possessed skill.
For example, a user can add a skill to their profile that they possess, such as HTML. They can also add a skill to their profile that they wish to learn, such as Ruby on Rails. On their profile it'll list their current skills and wanted ones, separately.
From a high level, what's the best way to architect this? I just want there to be 1 Skill model with no duplicates, but I want there to be a way for users to have the 2 separate groups of skills in the database.
You can use single table inheritance
class Skill < ActiveRecord::Base
end
class WantedSkill < Skill
belongs_to :user
end
class PossessesSkill < Skill
belongs_to :user
end
Your skills table should have a column called type where the type of the skill will be stored.
WantedSkill.create(:name => "html")
Above will save the record in the skills table with type 'WantedSkill'. You can retrieve it by
WantedSkill.where(:name => "html").first
your user associations can be like
class User < ActiveRecord::Base
has_many :wanted_skills
has_many :possessed_skills
end
You can read documentation here
A way to achieve this, you need two skill fields like: wanted_skill and possessed_skill
So, in Ruby on Rails you can have many references (with different names) to the same model, you only need to declare which class corresponds using class_name in the references, e.g.:
class User < ActiveRecord::Base
belongs_to :wanted_skill, class_name: 'Skill'
belongs_to :possessed_skill, class_name: 'Skill'
end
So I have an application with users/user-profiles. Presently, users can view others profiles, send messages, and favorite user profiles. I'd like to also add the feature to allow users to 'hide-user' profiles so that they won't see the user ever again in search, or anything. And provide the option in 'settings' to 'un-hide' the user as well.
How might I do this? I haven't a clue as to where to begin with this feature.
(If you need any code examples, models, controllers please ask and I will happily provide)
Kind regards,
Sonny
There are probably a couple of ways to do this, the first approach that comes to mind would be to establish a self-referencing many to many relationship.
You will need to create the join table (I shall call it suppressed_users). I will show the rails model, as the migration wouldn't have anything other than the foreign keys.
class SuppressedUser < ActiveRecord::Base
belongs_to :user
belongs_to :suppressed_user, :class_name => "User", :foreign_key=>"suppressed_user_id"
end
And in the User model, in order to help DRY up your code, you can use a scope to easily filter all the users that this target user has decided to suppress (or hide):
class User < ActiveRecord::Base
has_many :suppressed_users // Optional
scope :without_hidden_users, -> (target_user) do
where.not("exists (?)",
SuppressedUser.select("1")
.where("users.id = suppressed_users.suppressed_user_id AND suppressed_users.user_id = ?", target_user))
end
end
Note about the scope: What I'm doing here is creating a dependent (or correlated) subquery, in which I check whether the target user has suppressed (or hidden) the user we're looking at at the moment. In other words, the dependent subquery is executed for each row in the users result set (those not filtered by other where or join conditions and such). With proper indexing, this should not have an impact on performance.
Note about has_many :suppressed_users: This is technically not needed for the query I've shown, so if it is not relevant for anything in your system, you should be safe to remove it.
So, if I am presently logged in, and I want to search for a list of users meeting some condition, in your controller you would do something like this:
User.without_hidden_users(#current_user.id)...other conditions and such as needed
Assuming #current_user represents the currently logged in user.
I believe one approach would be to create a many_to_many relationship via the has_many :through association between users and hidden users. And likewise another many_to_many relationship between users and hiders (i.e. users who are hiding the user). In the end, you should be able to do something like this:
some_user.hidden_users and some_user.hiders (i.e. all the users that are hiding some user). You probably won't need the second one most of the time.
So the first thing to do would be to create a join table called hiders_hidden_users (or whatever you want) which would contain only two fields: hider_id and hidden_user_id.
Note, that in reality, those ids will both refer to a User record. Your HidersHiddenUser model will look something like this:
class HidersHiddenUsers < ActiveRecord::Base
belongs_to :hidden_user, class_name: "User", foreign_key: "hidden_user_id"
belongs_to :hider, class_name: "User", foreign_key: "hider_id"
end
Then, you need to set up the relationships in the User class:
class User < ActiveRecord::Base
has_many :hiders_join, class_name: "HidersHiddenUser", foreign_key: "hider_id"
has_many :hidden_users_join, class_name: "HidersHiddenUser", foreign_key: "hidden_user_id"
has_many :hiders, through: :hiders_join
has_many :hidden_users, through: :hidden_users_join
end
Note, that you have to specify the class name and foreign key when writing the has_many relationship with the join table. Note, also that you have to specify the relationship twice with the same model.
Then, say some_user wants to hide user1 and user2. All you would need to do is something like this:
some_user.hidden_users << user1
some_user.hidden_users << user2
Although I have not tested this, I believe it should work.
lets say I have the Users table, and the Team table.
In Rails, I know how to link the user_id column in the Team table to the Users table. But what if I have a second column I also want to link to the user's table, such as user_id2 (this essentially creates an order in the team table)?
Is there a solution, or something I don't know about to do what I'm trying? I also don't think the "has_many" is what I'm looking for, because user_id might be the team manager, and user_id2 might be the team captain, i.e. they have different roles affiliated with them, and order is important.
Thanks!
Edit: for my purposes, I also wouldn't need more than these two user relations. (i.e. cases for three wont be relevant)
You may want to look into STI (look for Single Table Inheritance on that page) or Polymorphic Associations. Either would allow you to express your intent a bit more clearly, although there isn't enough information in your question for me to puzzle out which would fit best.
Give those a read and see whether they accomplish what you want.
First here is a way to do this in one direction (Team -> User), but it wouldn't work for the reverse direction, and there's a better option I'll get into afterwards. The first one assumes you have columns named manager_id and captain_id on the teams table.
class User < ActiveRecord::Base
end
class Team < ActiveRecord::Base
belongs_to :manager, :class_name => ::User
belongs_to :captain, :class_name => ::User
end
However, I'd be surprised if a Team only consisted of two Users (the manager and captain) - it's more likely that you'd want a join table to track all of the users' team memberships. That join table (called team_memberships in this example) could have a role column that holds the manager/captain info, as well as any other data you have. This way is a lot more flexible, and offers additional benefits, like being able to track historical team data if team members change over time, which they will.
class Team < ActiveRecord::Base
has_many :team_memberships
has_many :users, :through => :team_memberships
def captain
team_memberships.captain.first
end
def manager
team_memberships.manager.first
end
end
class TeamMembership < ActiveRecord::Base
belongs_to :user
belongs_to :team
# You'll need some database-level UNIQUE INDEXes here to make sure
# you don't get multiple captains / managers per team, and also
# some validations to help with error messages.
named_scope :captain, :conditions => {:role => "captain"}
named_scope :manager, :conditions => {:role => "manager"}
end
class User < ActiveRecord::Base
# depending on the rules about teams, maybe these should be has_many...
has_one :team_membership
has_one :team, :through => :team_memberships
end
Check out http://guides.rubyonrails.org/association_basics.html for more details.
I'm creating a little social network in Rails, where people can add eachother as a friend. I have created a model called 'user', which contains, e-mail, strong md5 hash with salt of password etc.
How do I create something like an option to add another user as a friend? Is it possible to have something like has_many_and_belongs_to :user in the user model? So a user has many users and belongs to many users. Or should I use another way, like adding a friendship model which has user1s_id:integer and user2s_id:integer?
Essentially you want a join model that joins users to users. But you'll have to use more descriptive terms for Rails.
Twitter doesn't use Rails, but here's how their user associations might work in Rails using Twitter's terminology. Note: Twitter doesn't require bidirectional following (ie just because a user follows another, doesn't mean that second user follows the first)
class User < ActiveRecord::Base
has_many :followings
has_many :followers, :through => :followings, :class_name => "User"
has_many :followees, :through => :followings, :class_name => "User"
end
class Following < ActiveRecord::Base
# fields: follower_id followee_id (person being followed)
belongs_to :follower, :class_name => "User"
belongs_to :followee, :class_name => "User"
end
Forced bidirectional friendship (like Facebook enforces that I cannot be friends with you, unless you are a friends with me) will take a little more work. You will either need to manage reciprocal entries with callbacks, or use custom finder SQL. Using custom finder SQL means that ActiveRecord probably won't be able to manage associations for you.
I would suggest that a user has many relationships, this would leave you free to add new types of relationships in the future.
So you start with a "one user to another user" relationship table (minimally, just two IDs) and then in the future you could add a "one user to a group" relationship table (once you've created groups of course!)
I'd suggest using the friendship model because db-wise you'll need a join table either way, but making that table explicit will allow you to store more details about the relationship (e.g. "friend" or "family", date added, ...)
Rails has a has_one :through association that helps set up a one-to-one association with a third model by going through a second model. What is the real use of that besides making a shortcut association, that would otherwise be an extra step away.
Taking this example from the Rails guide:
class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, :through => :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ActiveRecord::Base
belongs_to :account
end
might allow us to do something like:
supplier.account_history
which would otherwise be reached as:
supplier.account.history
If it's only for simpler access then technically there could be a one-to-one association that connects a model with some nth model going through n-1 models for easier access. Is there anything else to it that I am missing besides the shortcut?
Logic, OK it might sound a bit weak for this but it would be logical to say that "I have a supplier who has an account with me, I want to see the entire account history of this supplier", so it makes sense for me to be able to access account history from supplier directly.
Efficiency, this for me is the main reason I would use :through, simply because this issues a join statement rather than calling supplier, and then account, and then account_history. noticed the number of database calls?
using :through, 1 call to get the supplier, 1 call to get account_history (rails automatically uses :join to retrieve through account)
using normal association, 1 call to get supplier, 1 call to get account, and 1 call to get account_history
That's what I think =) hope it helps!
I'm surprised no one has touched on Association Objects.
A has_many (or has_one) :through relationship facilitates the use of the association object pattern which is when you have two things related to each other, and that relation itself has attributes (ie a date when the association was made or when it expires).
This is considered by some to be a good alternative to the has_and_belongs_to_many ActiveRecord helper. The reasoning behind this is that it is very likely that you will need to change the nature of the association or add to it, and when you are a couple months into a project, this can be very painful if the relationship were initially set up as a has_and_belongs_to_many (the second link goes into some detail). If it is set up initially using a has_many :through relationship, then a couple months into the project it's easy to rename the join model or add attributes to it, making it easier for devs to respond to changing requirements. Plan for change.
Inverse association: consider the classic situation user-membership-group. If a user can be a member in many groups, then a group has many members or users, and a user has many groups. But if the user can only be a member in one group, the group still has many members: class User has_one :group, :through => :membership but class Group has_many :members, :through => memberships. The intermediate model membership is useful to keep track of the inverse relationship.
Expandability: a has_one :through relationship can easy be expanded and extended to a has_many :through relationship