This is probably a newbie question, but I cannot figure out the best solution.
I have a Color model and a Cover model.
Most of the time, a cover has_one :color. Maybe one out of one hundred cases, the cover has_many :colors.
Do I have to implement a has_many :through association between the two models? It seems redundant. What's the best practice to define such a relationship?
I think you should cover the general case, when Cover has_many :colors. So this is a many-to-many relationship. In this case, you will have 2 options:
Use join table, if you just need to store cover_id and color_id in the join table, so you will use has_and_belongs_to_many to define your association. With this option, You will have to create join table.
Use join model, if you need to store not only cover_id and color_id, but also other attributes, and you need to do some calculation, so you have to use has_many :through to define your association. With this option, You will have to create new model.
Cover has_many :colors would be best way, even if there is only one single case that requires it.
Related
I have a question about these two relationships as I think I'm overthinking it and have confused myself quite a bit.
So they both involve a new table that sits in between the two tables you want to join.
A simple M2M gets the foreign keys from the other two tables (so example would be "blog_posts_categories" table would get the blog_post_id and the category_id foreign keys). Then for the associations, the blog_post and category models would have has_and_belongs_to_many associations with each other but the joined table gets no model.
for a rich M2M, a third joined table is created. This table gets the foreign key from the other two tables, and those two tables get the foreign key of the joined table. Then for the rails association, the joined DOES get a model, and it belongs_to the other two corresponding models. And those two models has_many of the joined table model
Am I anywhere near close to being right? I think my issue is that I keep conflating the table with the model or at least for the simple many-to-many since I keep expecting there should be a model to go along with the table.
Thanks for any direction that can be given.
Your conceptual understanding of the two are almost there. Rather than thinking of them as simple vs. rich, I prefer to think of them as implicit vs. explicit.
Take for example, two models, Book and Author.
With a has_and_belongs_to_many, Rails will implicitly create a join table between your two models. I.e. books_authors.
You can also explicitly create the join table, say, Authorship, that belongs_to both. (Book and Author will then have has_many :authorships.)
In both cases, your domain model will look the same:
Book (1)--(n) Authorship (n)--(1) Author
Now, here is the opninionated part. I prefer to use the explicit approach, as this is easier to grasp, conceptually, and makes it easier to add additional fields to your join table. Say that for example you want the Authorship to be ordered, you can easily add an order column to your Authorship model and use that accordingly.
A third relationship table is suitable for many-to-many relations. You can do something like this:
class BlogPost < ActiveRecord::Base
has_many :blog_post_categories
has_many :categories, through: :blog_post_categories
end
class Category < ActiveRecord::Base
has_many :blog_post_categories
has_many :blog_posts, through: :blog_post_categories
end
class BlogPostCategory < ActiveRecord::Base
belongs_to :blog_post
belongs_to :category
end
The third model is very simple and its overhead is basically negligible. Also it is flexible and extensive if you want to attach more information to that relationship (for example, priority or timestamp). I personally prefer having a standalone table for relationships rather than a few more columns in the blog_posts table.
Here is a relevant blog post: Why You Don’t Need Has_and_belongs_to_many Relationships.
Perhaps you can explain the tradeoffs you're considering here?
I'm new to RoR, and I'm trying to create my first association within my app between an Order model, and a star key that represents all of the users who have starred that particular order.
I was originally thinking of using a has_and_belongs_to_many association for this, but that doesn't make sense given that there aren't many stars coming from the one user to one order.
With that said, I've really confused myself and could use a little bit of direction. Perhaps a simple migration file would send me down the right path.
Cheers!
Assuming users can "star" more than one order, then you do indeed want has_and_belongs_to_many. You can readily enforce that any one user has only one relationship to any one order.
I would indeed create an in between table referencing both Order and User, as an order has many users that keyed the order, and a user can key many orders...
rails g migration CreateOrderUsers order:references user:references
Your order_users model becomes
belongs_to: order
belongs_to: user
Then put in your order model
has_many :order_users, dependent: :destroy
has_many users, through: :order_users
And in your user model
has_many :order_users, dependent: :destroy
has_many orders, through: :order_users
Hope this helps!
I have read the Choosing Between has_many :through and has_and_belongs_to_many on the Rails website, however I am a bit confused since I have a different case to the ones given on the website.
I have two models: Prop and CharacterCostume, and the character's costume can have multiple props associated to it, but a prop doesn't belong to that character and it can be used by any number of characters in the scene, too.
Right now I have has_and_belongs_to_many :props inside my CharacterCostume model, which does exactly what I want it to do: it fetches all the props associated with the costume using a table named character_costumes_props when I call CharacterCostume#props
However the association name is putting me off because of the "belongs to many" part. The costume does not belong to any of the props, so there's no has_and_belongs_to_many :character_costumes inside the Prop model.
I know that it can all function fine without it, but it got me thinking that maybe I should use a has_many :through association, but that requires me to create a superfluous model (it is superfluous, right?) and the model would look like this:
class CharacterCostumeProp < ActiveRecord::Base
belongs_to :character_costume
has_one :prop
end
Also, would has_one instead of belongs_to work here?
I want the code to be as semantic as possible, but I am not sure if this will increase the requirement for resources or decrease performance in some way, since there's an intermediate model.
Are there certain quirks/benefits attached to either approach? Is mine good enough? Or is my thinking completely wrong from what I need to do?
Thanks!
I think you want to use a :has_many, :through because you're going to want to work directly with the relation model - what scene(s), consumed or damaged, etc.
But, the reason it reads funny to you is that, for the most part, has_many and belongs_to don't really mean what they mean in English. What they really mean is "They have the foreign keys" and "I have the foreign key", respectively; the exception being the :dependent => :destroy behavior.
That still doesn't really help with has_and_belongs_to_many, since you're then saying, "They have the foreign keys and I have the foreign keys` - except that you can think of it sort of adding a new part both to "I" and "They" that happens to be the same part for each, and has those keys.
Does that help?
The single most important question you must ask yourself when deciding between HABTM and has_many :through is this:
Do I want to store any information specific to the association?
Example 1: magazine subscriptions
A many-to-many relationship between readers and magazines might conceivably be structured as a HABTM or a has_many :through. However, the latter makes far more sense in this case because we can easily think of information specific to the association that we might want to store.
A reader is related to a magazine through a subscription, and every subscription can be described by fields such as price, starting date, issue frequency and whether it's active or not.
Example 2: tags
The relationship between an existing Tag model and, say, an Article model is clearly of the many-to-many kind. The fact that one particular tag has been associated to any particular article must have no influence on whether the same tag will be able to be similarly associated to other articles in the future.
But, differently from the previous example, here the association itself is all the information we need. We just need to know which tags are associated to any given article. It doesn't matter when the association was formed. It doesn't matter how long it lasted.
It may matter to us how many articles a tag is associated with. But that information is stored in the Tag model since it's not specific to an association. There is even a Rails feature that takes care of that called counter_cache.
has_one wouldn't work, you'd need belongs_to
it is not superfluous if you have logic in your association model
has_and_belongs_to_many is good enough for you
See example below
class Student
has_and_belongs_to_many :courses
has_many :teachers, through: :courses
end
class Teacher
has_many :courses
has_many :students, through: :courses
end
class Course
has_and_belongs_to_many :students
belongs_to :teacher
def boring?
teacher.name == 'Boris Boring'
end
end
In the example above, I make use of both versions. See how Course would have its own logic? See how a class for CourseStudent might not? That's what it all comes down to. Well, to me it is. I use has_many through for as long as I can't give a proper name to my association model and/or the model doesn't need extra logic or behavior.
So I'm trying to come up with a smart/the best rails way to do this, and would appreciate discussing with SO users the best method.
Essentially I have two models, let's call the first a Photo and the second an Attribute. A photo has_many attributes (as examples, let's give saturation, color, warmth etc..). The Attribute model carries and name and id. The Rating model carries a single integer field. The Photo and Attribute models are related through a PhotoRelationship model, through a polymorphic association. The reason for this is that a photo has_many other relationships that I store in this table (rather than having a multitude of relationship tables). Assume I have to use these models, as they are already in place, but I can alter them or add additional models. Here is how the models' relationships work:
in Photo.rb:
has_many :photo_relationships
has_many :attributes, :through => :photo_relationships, :source => :related_attribute, :source_type => "Attribute"
in PhotoRelationship.rb
belongs_to :photo #photo_id
belongs_to :related_attribute, :polymorphic => true
in Attribute.rb
has_many :photo_relationships, :as => :related_attribute, :dependent => :destroy
has_many :photos, :through => :photo_relationships
Let's say I also want to rate the Photo model's Attributes. I introduce a third model Rating, because each photo-attribute pairing will have a certain rating then associated with it.
I want to figure out how to run the relationship between a photo-attribute pairing and its rating. For example, let's say we have a photo with a certain number of attributes and we want to find the rating of, say, the first attribute of that photo. Then:
photo = Photo.first
photo.attributes.first.rating
How would I set it up in the database? I suppose I could add a rating_id field to the PhotoRelationship model, which already has unique entries for each photo-attribute pair.
Could this work, could I somehow relate the attribute to its rating?? Something tells me something is missing. I am getting the feeling that I would need to have a separate AttributeRelationship table that is non-polymorphic and even in this case, I don't see how the relationships work.
Any info anyone can provide, thoughts, or alternate suggestions on how to do this would be greatly appreciated! Thanks!
I suggest you use one of the gems already available that provide ratings functionality:
https://www.ruby-toolbox.com/categories/rails_ratings
If you want to add rating functionality to existing models from scratch then think about using mixins(modules), because your model associations are quite complicated as it is.
Useful info about mixin usage here:
ruby inheritance vs mixins
I've only created a has_and_belongs_to_many association before and it is different from has_many :through. For a has_many :through association, do I need a join table? How does the actual association work? Do I need an index? I can't find a great tutorial on this, any suggestions?
Yes you need the join table.
This shows how: http://railscasts.com/episodes/47-two-many-to-many
This may also be helpful; has_many :through questions
By the way if you need to search with condition this will help: Has many through associations with conditions
Also a great example with code of editing the nested attributes of the join table at Rails nested form with has_many :through, how to edit attributes of join model?.
All these are sorts of things you might find yourself wanting to do :)
Index are optional and also vary by db. mySQL used to only support 1 at a time. Not sure if that has changed.
It depends on how you want to use the has_many :through relation. There are two different cases (named in the guide to relations
for an n:m relation: the example in the guide is physicians have appointments with patients.
for an 1:n and an additional 1:n relation: the example in the guide is 1 document has many sections and 1 section has many paragraphs.
For the first one, you need the join table, you don't need the index. For the second one, you need none of them.