Linking 2nd row to different database table, in rails - ruby-on-rails

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.

Related

Rails has many through relationship

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.

How to associate existing model with new model in Rails

I have a 'tip' model and a 'team' model. I am trying to create a form where a user will select the winning teams from several games. So far the user can select the winning teams and those id's are being saved as foriegn keys in the Tip model (team_id). But after saving, I can't go (for example ) #tip.team.name . What do I need to do ? How do I associate it (I though rails might magically do it if foriegn key is set), I am very new to rails. Thanks for any help !
class Tip < ActiveRecord::Base
attr_accessible :user_id, :team_id, :team, :game_id, :game, :comp, :comp_id
belongs_to :game
belongs_to :comp
belongs_to :team
end
class Team < ActiveRecord::Base
attr_accessible :name
has_many :tips
end
def create
params['game'][0].each do |key, value|
#tip = Tip.new
#tip.team_id = value
#tip.game_id = key
#tip.save
end
This last method may be messy too, but not sure how else to do it. There are several 'tips' that I want to create in the one form.
EDIT : To be clear I am quite sure it's a one to many relationship, I am creating several tips in the one form but each tip only relates to one team.
EDIT : Actually my approach (which I'm sure is not close to the best way, did allow tip.team.name. It was a silly error relating to my test data that made me think otherwise.
What you really need is to use a has_many through relation to link the tips and the teams. This is because one tip can have many teams, but also one team can be on many tips. You will need to create a third table to do this, maybe named TeamsTips. Here is how you might set it up:
class Tip < ActiveRecord::Base
has_many :teams_tip
has_many :teams, :through => :teams_tip
end
class TeamsTip < ActiveRecord::Base
belongs_to: teams
belongs_to: tips
end
class Team < ActiveRecord::Base
has_many :teams_tip
has_many :tips, :through => :teams_tip
end
Now when you have a #tip, you can find all the teams for it with #tip.teams. Remember, this will be an array so to get the first team use #tip.teams[0]. Likewise, if you have an #team, you can get all the tips for it with #team.tips.
For more informations on how to setup this has_many through association, see A Guide to Active Record Associations
if ur asscociation is "Tip belongs to a Team " and "Team can have many Tips" then the association u defined in the question is correct.
if u want to created multiple tips when a team is created or add/edit/delete tips for a already created team, have a look at "accepts_nested_attributes_for" and https://github.com/ryanb/nested_form.
If u can get the team name using '#team.name' then u should get it using "#tip.team.name"

Rails employer and employee devise

I am trying to figure out how to structure this please help:
I need users to be able to sign up as either an employer or employee. The employer basically is the company and the employees are the people employed at that job. I want to know how to keep a record of when/if the employee was hired and terminated from the job. Would a has_many:through assotiation work for this or better yet is there a gem that I am overlooking that could help me with this?
This is a classic case of the hierarchical object model, so you have to think about this problem in terms of the objects involved and their relationships to each other. Think about the way a business works in real life:
Employer -> Employees NOT Employer -> Manager etc. -> Employees
A good example of this system model is that of GitHub. On GitHub, users can belong to Organisations. They can also create them and administrate them, managing members etc. (hiring and firing in your case). Therefore a much better way of modelling this system is having it be User-centric rather than a split of two different classes of User.
As stated in a previous answer, Employers (or Businesses in this
sense) should not be considered as users of the system as they won't
be acting on the state of the program, users will. Therefore, I personally think STI is a
little overkill for this system model.
So...
All people employed in a business are employees, but not all employees have the same level of authority (Managers will have more authority than Junior employees for example). Therefore you should model a Business, which has many Employees. Those Employees will have varying levels of authority based on their position:
can_hire?
can_fire?
etc.
You could also create a method that would tell you when this Employee was hired/fired. These could return a date/time if they were hired/fired or nil if not. nil in this sense would obviously imply they haven't yet been hired/fired.
When it comes to managing the abilities of different users, their level of authority is defined by what they can and cannot do. You can of course apply preset roles to this situation with a simple method that checks the above methods. For example:
def manager?
self.can_hire? and self.can_fire?
end
You would then also define an ability that would allow the creation of a Business:
def can_create_business?
self.manager?
end
Note: you can use scopes to achieve this elegantly.
You can also sub-class a basic User model for your roles, or create a Role class that defines the above methods and acts on a given User model. You can then create individual roles and subsets of roles.
Arguably, there are some cases where allowing the creation of employers/businesses and employees as separate entities that can both act on the state of the program are useful. If the Business can perform more than just simple administration functions then this might be worth it. In which case, I would still not consider the business as a User model. I would require a special user account be created along with the Business which would have a role of Business Administrator or something similar.
This model follows some of the most basic principles in programming and Computer Science as a whole, so I hope it sheds a little light on your problem. Of course there are a number of gems available that have this functionality built-in, namely Devise and CanCan/CanTango, although I would personally build this system myself.
I think I will combine STI with has many through relationship.
I will first start by making a single table inheritance of Employers and Employees.
class User < ActiveRecord::Base
...
end
class Employer < User
end
class Employee < User
end
Employer hires employees. One employer can have many employees and with employment come other related properties like date_of_hire, designation, status, manager_id, department etc. Hence I would model this as a separate object and hence store this information in a separate table. Let's call that Employment, shall we?
rails g model employment employer_id:integer, employee_id:integer, date_of_hire:date, designation:string, status:string, manager_id:integer
Let's establish relationships now.
class Employment < ActiveRecord::Base
belongs_to :employer # Foreign key options should not required because of User STI
belongs_to :employee
belongs_to :manager, :foreign_key => :manager_id, :class_name => "User"
end
class Employee < User
has_many :employments
has_many :employers
has_one :manager, :through => :employments, :foreign_key => :manager_id
end
class Employer < User
has_many :employments, :foreign_key => :employer_id
has_many :employees, :through => :employments
end
Based on business rules we can implement elegant scopes.
class Employee < User
...
scope :fired, :where => {:status => 'fired'}
scope :recently_joined, :where => {:date_of_hire => 3.months.ago...Date.today}
scope :developers, :where => {:designation => 'developer'}
...
end
and...
Employee.recently_joined
Employee.recently_joined.developers
Employee.fired
Please understand that this is obviously not a tested code and may have some glitches.
However!
I would highly encourage you to re-consider the need of modeling employers as users. From my personal experience, it turned out to be disaster in the future (May be I was inexperienced then but I won't really take that route again). I would really make separate models for Employer and Employee and establish relationships as above but with foreign_key and class_name attributes. The major reason is that for STI to work, in your domain, the employer and employee should have an "IS A" relationship with User. You might be tempted to think that it is an "IS A" relationship but also think whether it's the same "TYPE" of "IS A" relationship. In the application I worked on, it did not make sense and yet we settled with STI. The employers had completely different set of features, identity and treatment in the application from the employees. Although there was some data that was the same for both, it existed for different purposes and was used different. That is a reason enough (Single Responsibility) to model them separately. STI is a tricky tool that can solve a problem. But use it incorrectly and it will create more problems than what it would solve.
Just use has_many, :through. It will get the job done. Say you need to get all the active employees for a single company you can do
class Employers
has_many :hired_employees, :through => :employments,
:class_name => "Employees",
:source => :employee,
:conditions => ['employment.status= ?',"hired"]
end
Then you can do Employers.first.hired_employees. You can add more and use different conditions so that you can get 'terminated', 'dismissed' etc.
Of course this assumes you have a third model called Employment where it belongs to Employee and Employer. Your Employee and Employment classes could look like this:
class Employee
has_many :employments
end
class Employment
belongs_to :employee
belongs_to :employer
end
From what you said, you may need the following model: User, Job, Assignment, Role
class User
... some devise declarations here ...
... some role here. rolify gem is my suggestion ...
scope :employee, :where => {user.has_role? :employee}
scope :employer, :where => {user.has_role? :employer}
has_many :jobs, :through => :assignments
has_many :assignments
end
class Job
belongs_to :user # employer
has_many :assignments
has_many :users, :through => :assignments # employee
end
class Assignment
belongs_to :job
belongs_to :user # employee
def fired?
... stuff to check if the employee is fired
end
def hired?
... stuff to check if the employee is hired
end
end
Pros:
you can add more roles such as manager, admin ... easily. Using rolify gem or you can just create the role model for yourself
the assignment class will stored anything you need to store such as fired, hired about the employee
since the roles are clearly predefined, access control can be added easily using authorization gem such as cancan.
Cons:
Using this model, use must check whether the current user you are working on is an employer or an employee before doing anything else. However, this can be easily be checked using roles
For simplicity, you can use rails_app_composer gem to generate the initial app with devise, cancan and rolify installed.
NOTE: this answer is based on many similar questions such as:
Polymorphism or STI

How would you arrange your models?

In a Rails project, i need a user to have an inventory and a warehouse. Items can be exchanged. The question is, what is the most efficient way to handle this ? I thought of :
User has one inventory -> inventory has many items through inventory_item
User has one warehouse -> warehouse has many items through warehouse_item
However, i'm not so sure whether this is the most efficient way. Another idea would be so have a user_item with a field like location that can either be inventory or warehouse.
What do you think is the best practice ?
I would probably do a combination of both. I would have an association for the owner for every item and then an association for the place or position of the item. Both of those would probably be polymorphic. I think that would be the best way to fit some of the likely scenarios like:
An item is owned by a player and is placed in the players inventory
An item is owned by a player and is placed in the players warehouse
An item is owner by a player and is equiped by a player (perhaps this is still inventory)
An item is owned by a guild (if this is something you may want in the future) but in a players inventory (borrowed) or equipped
Thinking of these scenarios I think it is more db query effective to be able to load all items owned by a player without looping through inventories, warehouses etc or to be able to get all items equipped by a player without looping through different owners etc.
Edit
Here is an example:
class User < ActiveRecord::Base
has_one :inventory
has_one :warehouse
has_many :owned_items #:as => :owner ???
has_many :items, :through => :owned_items
end
class OwnedItem < ActiveRecord::Base
belongs_to :user #:polymorhpic => :true ???
belongs_to :position, :polymorphic => true # Could be inventory, warehouse or someplace else
belongs_to :item
end
class Inventory < ActiveRecord::Base
belongs_to :user
has_many :owned_items, :as => :position
end
class Inventroy < ActiveRecord::Base
belongs_to :user
has_many :owned_items, :as => :position
end
In the above example I left out the possibility of a guild or something similar being the owner of an item but I added comments on where it could easily be adapted to support such a thing.
So if you look at the OwnedItem class, position is the polymorphic association the specifies where the item is located at the moment (an inventory, a warehouse, etc) and then it also belongs_to a user which specifies who the owner of the item is. Now you asked about it being redundant and I assume you mean that it could potentially be found by checking the item's inventory and see who that inventory belongs_to. Yes, of course that is possible. But then it would not be possible to have an item in the inventory that does not belong to you, i.e. a "guild item".
But even if you choose not to add something like guilds, I would still recommend to have the OwnedItem belong_to a user. An example to support that would be if you plan on some items being "unique" as in a user can only have one item of that kind. Instead of check both the invetory and warehouse (and possible something else) you can just specify:
validates_uniqueness_of :item_id, :scope => :user_id
in the OwnedItem class and you're good to go.
My point being that all restrictions, queries and comparisons can be made more db time saving if you have both an owner and a position for an item.

RoR: "belongs_to_many"? Association headache

I can't seem to wrap my head around this, so I thought I'd post and see if anyone could help me out (please pardon the question if it's insultingly simple: it's complicated to me right now!)
I have these models:
order
service
customer
I think they speak for themselves: a service is what the customer buys when they place an order.
Ok.
So, naturally, I setup these relationships:
# a customer can have many orders
class Customer
has_many :orders
end
# an order belongs to a single customer and can have many services
class Order
belongs_to :customer
has_many :services
end
... but here's where I trip up:
# a service can belong to many orders
class Service
# belongs_to :order ???
end
Because my understanding of belongs_to is that--if I put it there--a service could only belong to one order (it would have only one value in the order_id key field--currently not present--tying it to only one order, where it needs to be able to belong to many orders).
What am I missing here?
There are two ways to handle this. The first is a rails-managed many-to-many relationship. In this case, you use a "has_and_belongs_to_many" relationship in both the Order and Service models. Rails will automatically create a join table which manages the relationships. The relationships look like this:
class Order
has_and_belongs_to_many :services
end
class Service
has_and_belongs_to_many :orders
end
The second way is to manage the join table yourself through an intermediate model. In this case, you might have another model called "LineItem" that represents a Service in the context of an Order. The relationships look like this:
class LineItem
belongs_to :order
belongs_to :service
end
class Order
has_many :line_items
end
class Service
has_many :line_items
end
I prefer the second myself. It's probably just me, but I don't get as confused about what's going on when it's explicit. Plus if I ever want to add some attributes to the relationship itself (like perhaps a quantity in your case) I'm already prepared to do that.
class Customer
has_many :orders
end
class Service
has_many :orders
end
class Order
belongs_to :customer
belongs_to :service
end
The Order should have customer_id and service_id, because it is in a many-to-one relationship with both.
I think this Railscast will help you out - basically you have 2 options. You can use has_and_belongs_to_many or has_many :through.
You will also find that has_and_belongs_to_many has been deprecated in favor of has_many :though => model_name which gives the same (and more) functionality.
I think you have realized this but your order is really a composite domain model, sometimes called an aggregate in DDD speak.
Your Service is a really a listing of some product/service that someone can order. Your order aggregate records what someone ordered.
The aggregate as someone else said is made up of a header, the Order, which includes things like who ordered, the date, does it include taxes, shipping charge, etc. And the Order has_many OrderLineItem's. The OrderLineItem belongs_to Service and contains things like the quantity ordered, belongs_to the product/service, etc.
class Customer < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :customer
end
class OrderLineItem < ActiveRecord::Base
belongs_to :Order
end
I personally use the OrderLineItem model name in deference to LineItem because in a system that needs to ship real products, as opposed to services, you might have allocations that link orders to inventory which would have line items and shipments that get the allocated product to the client, which also have line items. So the line item term can become very overloaded. This likely is not the case in your model because you're only doing services.

Resources