I have four models
Bid Order User Printer
currently I cannot access the order through anything except User.
I would like to be able to do something of the sort bid.order but have yet to figure out the correct association. Any thoughts?
class Bid < ActiveRecord::Base
belongs_to :printer
end
class Order < ActiveRecord::Base
belongs_to :user
has_many :bids
end
class Printer < ActiveRecord::Base
has_many :orders, through: :bids
has_many :bids
end
class User < ActiveRecord::Base
has_many :orders
end
Both models must know about the relationship so you need to state that in the Bid model
class Bid < ActiveRecord::Base
belongs_to :printer
belongs_to :order
end
Related
The association between class are as below
class Level < ActiveRecord::Base
has_many :levels_users
end
class LevelsUser < ActiveRecord::Base
belongs_to :level
has_many :schedules
end
class Schedule < ActiveRecord::Base
belongs_to :levels_user
end
I know the value of level.id. How do I find all schedules belonging to that level.id. level.id is the id of one of the level. I tried something like this:
Level.find(level.id).levels_users.schedule
But it won't work.
You could use through property of has_many association.
class Level < ActiveRecord::Base
has_many :levels_users
has_many :schedules, through: :levels_users
end
Now you can use
level.schedules
I have 3 different Models
Bid Order Printer
class Printer < ActiveRecord::Base
has_many :bids
end
class Order < ActiveRecord::Base
belongs_to :user
has_many :bids
end
class Bid < ActiveRecord::Base
belongs_to :printer
end
I would like to be able to view all of a printers orders they have bidded on, but with my current structure I cannot do it. it would be something like #printer.bids.orders
How can accomplish this? Would I have to change Order to have belongs_to Printer, or has_many: bids through orders? Something along those lines?
You would have to declare printer like this:
class Printer < ActiveRecord::Base
has_many :bids
has_many :orders, through: :bids
end
This should work
Here is my models:
class User < ActiveRecord::Base
has_many :products
has_many :comments
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
I need to get comment records from current user products only
How do I do that? thanks
If we move the relationships to use a has_many: comments, through: products you can probably get what you're after:
class User < ActiveRecord::Base
has_many :products
has_many :comments, through: products
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
Now you can do user.comments.
The rails docs are here, which say:
A has_many :through association is often used to set up a many-to-many
connection with another model. This association indicates that the
declaring model can be matched with zero or more instances of another
model by proceeding through a third model. For example, consider a
medical practice where patients make appointments to see physicians.
The Ruby on Rails app I am working on allows users to create and share agendas with other users.
In addition, we must be able to:
Display a list of agendas for each user, on his profile
Display a list of users associated with an agenda, on the agenda's page
When sharing an agenda with another user, define a role for this user, and display the role of this user on the list mentioned right above
I was going to go with a has_and_belongs_to_many association between the user and the agenda models, like that:
class User < ActiveRecord::Base
has_and_belongs_to_many :agendas
end
class Agenda < ActiveRecord::Base
has_and_belongs_to_many :users
end
But then I wondered whether this would let me get and display the #user.agenda.user.role list of roles on the given agenda page of a given user.
And I thought I should probably go with a has_many :through association instead, such as:
class User < ActiveRecord::Base
has_many :roles
has_many :agendas, through: :roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class Agenda < ActiveRecord::Base
has_many :roles
has_many :users, through: :roles
end
And although I was pretty comfortable about the idea of a user having several roles (one for each agenda), I am not sure about the idea of an agenda having several roles (one for each user?).
Finally, to add to the confusion, I read about the polymorphic association and thought it could also be a viable solution, if done this way for instance:
class Role < ActiveRecord::Base
belongs_to :definition, polymorphic: true
end
class User < ActiveRecord::Base
has_many :roles, as: :definition
end
class Agenda < ActiveRecord::Base
has_many :roles, as: :definition
end
Does any of the above solutions sound right for the situation?
UPDATE: Doing some research, I stumbled upon this article (from 2012) explaining that has_many :through was a "smarter" choice than has_and_belongs_to_many. In my case, I am still not sure about the fact that an agenda would have many roles.
UPDATE 2: As suggested in the comments by #engineersmnkyn, a way of solving this would be to go with two join tables. I tried to implement the following code:
class User < ActiveRecord::Base
has_many :agendas, through: :jointable
end
class Agenda < ActiveRecord::Base
end
class Role < ActiveRecord::Base
end
class Jointable < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
has_many :agendaroles through :jointable2
end
class Jointable2 < ActiveRecord::Base
belongs_to :roles
belongs_to :useragenda
end
I am not sure about the syntax though. Am I on the right track? And how should I define the Agenda and the Role models?
UPDATE 3: What if I went with something like:
class User < ActiveRecord::Base
has_many :roles
has_many :agendas, through: :roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class Agenda < ActiveRecord::Base
has_many :roles
has_many :users, through: :roles
end
and then, in the migration file, go with something like:
class CreateRoles < ActiveRecord::Migration
def change
create_table :roles do |t|
t.belongs_to :user, index: true
t.belongs_to :agenda, index: true
t.string :privilege
t.timestamps
end
end
end
Would I be able to call #user.agenda.privilege to get the privilege ("role" of creator, editor or viewer) of a given user for a given agenda?
Conversely, would I be able to call #agenda.user.privilege ?
Okay I will preface by saying I have not tested this but I think one of these 2 choices should work well for you.
Also if these join tables will never need functionality besides a relationship then has_and_belongs_to_many would be fine and more concise.
Basic Rails rule of thumb:
If you need to work with the relationship model as its own entity, use has_many :through. Use has_and_belongs_to_many when working with legacy schemas or when you never work directly with the relationship itself.
First using your example (http://repl.it/tNS):
class User < ActiveRecord::Base
has_many :user_agendas
has_many :agendas, through: :user_agendas
has_many :user_agenda_roles, through: :user_agendas
has_many :roles, through: :user_agenda_roles
def agenda_roles(agenda)
roles.where(user_agenda_roles:{agenda:agenda})
end
end
class Agenda < ActiveRecord::Base
has_many :user_agendas
has_many :users, through: :user_agendas
has_many :user_agenda_roles, through: :user_agendas
has_many :roles, through: :user_agenda_roles
def user_roles(user)
roles.where(user_agenda_roles:{user: user})
end
end
class Role < ActiveRecord::Base
has_many :user_agenda_roles
end
class UserAgenda < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
end
class UserAgendaRoles < ActiveRecord::Base
belongs_to :role
belongs_to :user_agenda
end
This uses a join table to hold the relationship of User <=> Agenda and then a table to join UserAgenda => Role.
The Second Option is to use a join table to hold the relationship of User <=> Agenda and another join table to handle the relationship of User <=> Agenda <=> Role. This option will take a bit more set up from a CRUD standpoint for things like validating if the user is a user for that Agenda but allows a little flexibility.
class User < ActiveRecord::Base
has_many :user_agendas
has_many :agendas, through: :user_agendas
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
def agenda_roles(agenda)
roles.where(user_agenda_roles:{agenda: agenda})
end
end
class Agenda < ActiveRecord::Base
has_many :user_agendas
has_many :users, through: :user_agendas
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
def user_roles(user)
roles.where(user_agenda_roles:{user: user})
end
end
class Role < ActiveRecord::Base
has_many :user_agenda_roles
end
class UserAgenda < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class UserAgendaRoles < ActiveRecord::Base
belongs_to :role
belongs_to :user
belongs_to :agenda
end
I know this is a long answer but I wanted to show you more than 1 way to solve the problem in this case. Hope it helps
Suppose I have 3 Models like this (not sure if this is correct):
class User < ActiveRecord::Base
has_many :lessons
has_many :points, through: :progress
end
class Progress < ActiveRecord::Base
belongs_to :user
has_many :lessons
end
class Lesson < ActiveRecord::Base
belongs_to :progress
end
(The Progress table has user_id and lesson_id fields.)
How would I make it so calling #user.points would return the amount of entries into the Progress table. Also, how would I build a relationship?
class User < ActiveRecord::Base
has_many :progresses
has_many :lessons, through: :progresses
end
class Progress < ActiveRecord::Base
belongs_to :user
belongs_to :lesson
end
class Lesson < ActiveRecord::Base
has_many :progresses
end
First, you need to set up the association for progress on your User model, so that the through association will work:
class User < ActiveRecord::Base
has_many :lessons
has_many :progress
has_many :points, through: :progress
end
Then you'll need to define a method (or relation) of points on your Progress table. Or, if you simply want a count of records, you could do: #user.points.size