My association option does not appear to be honored.
class ClassRoom < ActiveRecord::Base
has_many :class_assignments, :dependent => :destroy
has_many :people, :through=>:class_assignments
class Person < ActiveRecord::Base
has_many :class_assignments, :dependent => :destroy
has_many :class_rooms, :through=>:class_assignments
class ClassAssignment < ActiveRecord::Base
belongs_to :person
belongs_to :class_room
That is to say, when either a person, or a class room is deleted, the record in the join table/model should also be deleted.
However, ClassRoom.last.destroy destroys the ClassRoom, but no any of it's associated ClassAssignments.
I know :dependent=>:destroy is ignored when :through is used, but I should be able to use it on the join model right?
try
class ClassRoom < ActiveRecord::Base
def before_destroy
self.class_assignments.destroy_all
end
same for person
Related
I want a user to be able to create and save or choose an existing client to add to an invoice. There can only be one client per invoice.
I currently have 3 models users invoices and items
I am using a simple has_many relationship currently but I am getting confused now that I want to add a new table clients. I was hoping I could get some advice on what association to use.
My current associations
class User < ActiveRecord::Base
has_many :invoices
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :items, :dependent => :destroy
class Item < ActiveRecord::Base
belongs_to :invoice
So I was thinking to do something simple like adding has_many :clients
to users, add has_one :client to invoices and add the table clients
class User < ActiveRecord::Base
has_many :invoices
has_many :clients
class Client < ActiveRecord::Base
belongs_to : user
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :items, :dependent => :destroy
has_one :client
Would this work? Is there a better way?
It is very rare that you will use has_one. In your case the following models make more sense:
class User < ActiveRecord::Base
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :user
belongs_to :client
has_many :items, :dependent => :destroy
end
class Item < ActiveRecord::Base
belongs_to :invoice
end
class Client < ActiveRecord::Base
has_many :invoices
has_many :items, through: :invoices
end
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
How can I add ownership in many to many relationships?
For example like this models.
class User < ActiveRecord::Base
has_many :editabilities, dependent: :destroy
has_many :files, through: :editabilities
end
class File < ActiveRecord::Base
has_many :editabilities, dependent: :destroy
has_many :users, through: :editabilities
end
class Editabilities < ActiveRecord::Base
belongs_to :user
belongs_to :file
end
And I want to add a one-to-many relationship to User-and-Files.
At first I thought it is best to add owner boolean column to Editabilities, but I have no idea how to handle it.
Secondly I thought if I make a new junction model Ownerships, then I can handle it same way as Editabilities. But I've got a uninitialized constant User::Ownership when I tried it with code like this.
class User < ActiveRecord::Base
has_many :editabilities, dependent: :destroy
has_many :ownerships, dependent: :destroy
has_many :files, through: :editabilities
has_many :owned_files, through: :ownerships, source: :file
end
class File < ActiveRecord::Base
has_many :editabilities, dependent: :destroy
has_many :ownerships, dependent: :destroy
has_many :users, through: :editabilities
has_one :owner, through: :ownerships, source: :user
end
class Editabilities < ActiveRecord::Base
belongs_to :user
belongs_to :file
end
class Ownerships < ActiveReord::Base
belongs_to :user
belongs_to :file
end
How can I implement a feature like this?
The only problems I see here are the classes Editabilities and Ownerships. By Rails convention model class names should be singular, not plural.
class Editability < ActiveRecord::Base
belongs_to :user
belongs_to :file
end
class Ownership < ActiveReord::Base
belongs_to :user
belongs_to :file
end
One way to quickly check your class names is by checking the result of the classify function:
> "editibilities".classify
=> "Editibility"
> "ownerships".classify
=> "Ownership"
The rest of the associations all look correct.
Your class should be named Ownership and not Ownerships.
ActiveRecord class names are normally singular and table names are plural.
Easier solution seems to add belongs_to association to File model. Because 1 File can have only 1 owner.
class File < ActiveRecord::Base
...
belongs_to :owner, class_name: 'User'
end
You will need to add owner_id column to files table.
I'm a beginner in Rails and I have a problem with ActiveRecords associations.
I'm creating simple car rental service and I made the following associations:
class Client < ActiveRecord::Base
has_many :rentals
has_many :bookings
has_many :cars, :through => :rentals
has_many :cars, :through => :bookings
end
class Rental < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_one :car
end
class Booking < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_one :car
end
What I need is to have a car belonging to many bookings and rentals while every booking and rental can have only one car assigned.
class Car < ActiveRecord::Base
# belongs_to_many :bookings
# belongs_to_many :rentals
end
How should I do that?
If a car can have many bookings/rentals, but a booking/rental can only have one car, you're looking at a classic belongs_to/has_many situation. It looks like you're being tripped up by the distinction between belongs_to and has_one -- it's not a grammatical one, but a matter of where the foreign key column is located in your database.
belongs_to: "I am related to exactly one of these, and I have the foreign key."
has_one: "I am related to exactly one of these, and it has the foreign key."
has_many: "I am related to many of these, and they have the foreign key."
Note that has_one and has_many both imply there's a belongs_to on the other model, since that's the only option where "this" model has the foreign key. Note also that this means has_one should only be used when you have a one-to-one relationship, not a one-to-many.
Taking this into consideration, I would replace the has_one :car with belongs_to :car in both your Rental and Booking models, and place has_many :bookings and has_many :rentals in your Car model. Also ensure that your rentals and bookings tables have a car_id column; there should be no rental- or booking-related columns in your cars table.
Yes, there is a "belongs_to_many" in Rails, sort of. It's a little more work and you can't use generators with it. It's called a polymorphic association.
Even though you could make a car have many bookings & rentals, you could associate the car by making it belong to a polymorph such as rentable_vehicle. Your code would look like this
class Car < ActiveRecord::Base
belongs_to :rentable_vehicle, polymorphic: true
end
class Rental < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_many :cars, as: :rentable_vehicle
end
class Booking < ActiveRecord::Base
belongs_to :client, dependent: :destroy
has_many :cars, as: :rentable_vehicle
end
You can't do belongs_to_many. The closest you can really get is has_and_belongs_to_many, but I'm not sure that's what you want here - unless you can have multiple cars per rental/booking. Check out the guide for a full explanation.
I'd change it up like this:
class Rental < ActiveRecord::Base
belongs_to :client, dependent: :destroy
belongs_to :car
end
class Booking < ActiveRecord::Base
belongs_to :client, dependent: :destroy
belongs_to :car
end
class Car < ActiveRecord::Base
has_many :bookings
has_many :rentals
end
Also, I don't know how your rentals relate to bookings, but my immediate thought is that there should be some relationship between the two, because you probably can't have a rental without booking it, right?
A list has one owner (a user). A list also has a number of panelists (also users). I have tried defining the relationships between the three models: User, List, and Panelist. But I'm getting nowhere.
user.rb
class User < ActiveRecord::Base
has_many :lists
has_many :panelMemberships, :through => :panelists, :source => :lists
end
list.rb
class List < ActiveRecord::Base
belongs_to :user
has_many :panelMembers, :through => :panelists, :source => :user
end
panelist.rb
class Panelist < ActiveRecord::Base
belongs_to :list
belongs_to :user
end
I've tried all different combinations but nothing seems to work. Thanks in advance for any help you can provide.
The model also has to have a has_many relationship for whatever the through model is, so wherever you have has_many :x, through: :y, you also need to say has_many :y. You also shouldn't have a panelist model separate from your user model if panelists are users (unless you're doing STI, which you're not). From what I understand, you're trying to do something like this:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_and_belongs_to_many :lists # for the normal panelist / list relationship
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_and_belongs_to_many :users
end
Then you'll need to make a migration for a users_lists (with user id and list id) table which will be your join table but won't need its own model. But if you really want to keep the through relationship (good for if you do other stuff with the join model), then you'd do:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_many :panel_memberships
has_many :lists, through: :panel_memberships
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_many :panel_memberships
has_many :users, through: :panel_memberships
end
class PanelMembership < ActiveRecord::Base
belongs_to :user
belongs_to :list