I am working on a project where many ActiveRecord models can have a conversation associated with it. Users can discuss just about every aspect of the site. I have two ideas as to how this should be implemented.
1) Use a belongs_to in the asset, not the conversation - conversation will be totally unaware of its asset
class Product< ActiveRecord::Base
belongs_to :conversation
end
class PurchaseOrder < ActiveRecord::Base
belongs_to :conversation
end
2) Use a belongs_to, :polymorphic => true in the conversation
class Conversation < ActiveRecord::Base
belongs_to :asset, :polymorphic => true
end
class Product < ActiveRecord::Base
has_one :conversation, :as => :asset
end
class PurchaseOrder < ActiveRecord::Base
has_one :conversation, :as => :asset
end
Which is the correct way to model this relationship? If I were to state the relationship, I would say that "a product / purchase order may have one conversation".
I think it depends on what, if anything, one model in the relationship needs to know about the other. Seems to me, from your description that the second approach it more fitting in this case. Why?
The Product and PurchaseOrder models are self-contrained entities in the sense that they can exist apart from conversations about them. So you probably don't want foreign keys polluting these models for tacking on conversations. The relationship ought to be unobtrusive in that sense.
A Conversation has a logical dependency on the entity it's associated with so it has the asset_id (and asset_type) foreign keys and that's probably reasonable
This is a very common question and one which always has me stopping to think a bit too. It's not always obvious. There's a good article considering the issue here
Related
I'm trying to wrap my head around how I should model my database for a parent model with many has_one associations (20+). I have one model called House which is the parent model of all the other models:
class House < ActiveRecord::Base
has_one :kitchen
has_one :basement
has_one :garage
has_one :common_room
#... Many other child models
end
All the child models contain unique properties specific to their own class. I've thought about STI, but there isn't really any shared functionality or inputs that I can use across models. I've also thought of making one 'super model', but that doesn't really follow Rails best practices, plus it would contain over 200 columns. Is there another design pattern or structure that I can use to model this efficiently so that I can reduce database calls?
class House
has_many :rooms
Room::TYPES.each do |room_type|
has_one room_type, -> { where(room_type: room_type) }, class_name: "Room"
end
class Room
belongs_to :house
TYPES = %i/kitchen basement garage common_room etc/.freeze
end
In your migration make sure to add_index :rooms, [:type, :house_id], unique: true. Unless a house can have more than 1 type of room. If that is the case, I think a different approach is needed.
To your second question, it depends really, what type of database are you using? If its PostgreSQL you could use hstore and store those as a properties hash. Or you could serialize you db to take that as a hash. Or you could have another model that room has many of. For instance has_many :properties and make a property model that would store that information. Really depends on what else you want to do with the information
Why not using Polymorphism in Rails? It would be much simpler
class House < ActiveRecord::Base
has_many :properties, as: :property_house
end
class Kitchen < ActiveRecord::Base
belongs_to :property_house, polymorphic: true
end
class Garage < ActiveRecord::Base
belongs_to :property_house, polymorphic: true
end
For more information, go here: http://terenceponce.com/blog/2012/03/02/polymorphic-associations-in-rails-32/
I am working in Ruby on Rails 3. And trying to map out three models which mimic the data of a Company its employees and their respective departments.
In arrived at the following solution:
class Company < ActiveRecord::Base
has_many :departments
has_many :employees, through => :departments
end
class Department < ActiveRecord::Base
belongs_to :company
has_many :employees
has_one :department_description
end
class DepartmentDescription < ActiveRecord::Base
belongs_to :department
end
class Employee < ActiveRecord::Base
belongs_to :department
end
Is this the 'correct' way to associate these models?
I think your last response may explain why you are struggling to find a correct way to associate these models.
It seems that you see your Department merely as a join_table, and that may be due to the fact that you don't fully understand the has_many => :through construction and that it actually allows your Department to be a proper model with many attributes and methods in it, hence also a 'description' attribute.
To create a separate DepartmentDescription model is actually a waste of resource. Chad Fowler has a few good examples for :has_many => through and nested resources in his Rails Recipes... so check that out.
I am currently trying to set up a model structure that seems quite simple, but I haven't quite got it down.
I have a model payment that can belong to either a customer or a supplier (which can both have many payments).
My question is simply whether I need to manually create an interface table to allow this, or if declaring the polymorphic associations will do this for me?
e.g. I have:
class Payment < ActiveRecord::Base
belongs_to :payment_originator, :polymorphic => true
end
class Customer < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
class Supplier < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
Is this enough, or do I also need to use a generator to manually create the payment_originator model?
Thanks!
As far as the models go, this is good enough. You just need to migrate a :payment_originator_type and :payment_originator_id to the payments table. The associations you defined above will automatically fill these in for you.
I am trying to create an association between two tables. A student table and a computer table.
A computer can only ever be assigned to one student (at any one time) but a student can be assigned to multiple computers.
This is what I currently have in mind. Setting up a has-many through relationship and modifying it a bit.
class Student < ActiveRecord::Base
has_many :assignemnts
has_many :computers, :through => :assignments
end
class Computer < ActiveRecord::Base
has_one :assignment
has_one :student, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :student
belongs_to :computer
end
Does this seem like the best way to handle this problem? Or something better sound out quickly to the experts here. Thanks!
You need first to decide if a simple one-to many relationship is enough for you.
If yes, it gets a lot easier, because you can get rid of the Assignment-class and table.
Your database-table "computers" then needs a student_id column, with a non-unique index
Your models should look like this:
class Computer < ActiveRecord::Base
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :computers, :dependent => :nullify
end
"dependent nullify" because you don't want to delete a computer when a student is deleted, but instead mark it as free.
Each of your computers can only be assigned to a single student, but you can reassign it to a different student, for example in the next year.
Actually your approach is fine, as one offered by #alexkv. It is more discussion, than question.
Another thing if you want to use mapping table for some other purposes, like storing additional fields - then your approach is the best thing. In has_many :through table for the join model has a primary key and can contain attributes just like any other model.
From api.rubyonrails.org:
Choosing which way to build a many-to-many relationship is not always
simple. 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.
I can advise you read this, to understand what approach better to choose in your situation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
You can also use has_and_belongs_to_many method. In your case it will be:
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers, :join_table => 'assignments',
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student, :join_table => 'assignments',
end
or you can rename assignments table to computers_students and remove join_table
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student
end
Hey guys,
I'm new to rails
here are the 2 ways of making a Drummer model and Cymbal model both have many videos
1st way by using polymorphic:
class Drummer < ActiveRecord::Base
has_many :videos, :as => :videoable
end
class Cymbal < ActiveRecord::Base
has_many :videos, :as => :videoable
end
class Video < ActiveRecord::Base
belongs_to :videoable, :polymorphic => true
end
2nd way by using two 1:m association:
class Drummer < ActiveRecord::Base
has_many :videos
end
class Cymbal < ActiveRecord::Base
has_many :videos,
end
class Video < ActiveRecord::Base
belongs_to :drummer
belongs_to :cymbal
end
I haven't try them in console, But I think both will work as they should. But I don't know the difference?
I believe that you must use polymorphic method because a model cannot belongs_to (one to one association) more than one other model. For more info see this rails guide: http://guides.rubyonrails.org/association_basics.html
It depends on the columns you have in your database. If you have videoable_type and videoable_id you're doing polymorphism. In that case, calling videoable on an instance of Video can return anything, it's not related to drummers or cymbals. If it is drummer_id and cymbal_id it's the latter version you described.
Both will work.
Imagine you have 5 models sharing Videos.
You would need 5 modelName_id columns in videos.
To determine which type of parent model you have on Video you would need to check each one for id presence.
Validating that only one of the ids is set would be necessary (or valuable).
Polymorphic relations are easier to maintain and expand in such cases.