I'm trying to understand and implement Active Record Associations in Rails and am having some trouble understanding how to put together the specific relationships I need.
I have a Recipe model and an Ingredients model. Many Ingredients will belong to a single Recipe and therefore, a Recipe will have many Ingredients. I am having trouble grasping how this is handled through MySQL and how to implement these relationships in the models correctly. Here is the (relatively sparse) code I have, so far:
models/recipe.rb
class Recipe < ActiveRecord::Base
has_many :ingredients
end
models/ingredient.rb
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :recipes
end
However, I'm fairly certain the association line in ingredient.rb is incorrect.
How would I correctly implement these relationships?
Your Recipe model should have a has_and_belongs_to_many relationship with the ingredients instead of has_many. This allows a single recipe to have many ingredients (i.e. you can do #recipe.ingredients), while a single ingredient can be in many recipes (#ingredient.recipes).
It does seem kind of weird when first starting out, but once you've grokked how Rails relationships work, it becomes intuitive. You're on the right track.
A has_many relationship implements a one to many mapping while has_and_belongs_to_many implements a many to many mapping. So a has_many is paired with a belongs_to while has_and_belongs_to_many are paired together.
The relationship between Recipe and Ingredients will be a many to many one if you also want to find out relations like which recipes a particular ingredient is being used in.
To implement a has_and_belongs_to_many in a mysql db you will have to create a third join table that maps all the links between the two tables.
You can look at this stackoverflow question to get a better idea of the format of the join table.
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 am wondering how do I add additional properties to a many to many relationship.
I have two models that share a many to many relationship, Company and Profession.
Many professionals could belong to a Company
So my Company model looks like below
class Company < ActiveRecord::Base
has_and_belongs_to_many :professions
end
The same people in the same profession could belong to multiple companies as well
so
class Profession < ActiveRecord::Base
has_and_belongs_to_many :companies
end
Now I need to associate an hourly rate which could be different for each of the companies for the same profession. I am not very sure where to introduce the hourly rate property? Even if I were to add that to the joining table, how do I access that rate using active record?
This is a typical scenario where you select has_many through over habtm. As a rule, if you only need to associate two models, no other info needed to be stored in the association, use habtm. For most cases, you have to use has_many through. You case falls under this scenario.
You want to save the hourly rate in the table that associates a Profession and a Company. If you have existing data that you want to migrate, you may want to look at this post How to migrate has_and_belongs_to_many to has_many through?. If you can drop the joins table you use for the habtm association, just drop it and create a new table.
I’m creating an app that allows a user to create a course and include different types of resources (document, video, quiz) in the course. I’m having a hard time figuring out the best way to set up association between a course and its resources.
Right now, I have:
class Course < AR::Base
has_many :documents
has_many :videos
has_many :quizzes
end
class Document <AR::Base
belongs_to :course
end
class Video <AR::Base
belongs_to :course
end
class Quiz <AR::Base
belongs_to :course
end
Documents, Videos, and Quizzes all have some common attributes (e.g., name, description) but also many attributes that differ. I considered using STI and having them inherit from a single class called “Lessons.” I decided against this approach because I need multiple controllers for each class.
Ultimately, I want to be able to perform operations on all the resources that belong to a course so for example list all the documents, videos, and quizzes together and display them as a sortable list.
Any suggestions on how best to set up this model. It seems as if I could do it two ways:
Reverse polymorphic has_one association (see example)
Multiple Table inheritance (see example)
I’m a rails noob so I’m having a hard time evaluating which approach is best for this situation. Any advice would be appreciated!
Ultimately, I want to be able to perform operations on all the resources that belong to a course so for example list all the documents, videos, and quizzes together and display them as a sortable list.
Either approach is doable, but when I read this requirement, it seems you'd be best off with STI. STI is a natively supported feature in rails and allows you to query and manipulate different types of resources within the same collection.
Ruby on Rails App.
I have two kinds of users,
Company
has_many :employees #sample
Employee
has_one :company
I want to create friendships between these two models, keeping track of the requester of the relationship (.requested_relationships) and the receiver (.pending-relationships) as well as the status (accepted, pending, etc). I can easily create the relationship with a third model and has_many :through ... BUT that wouldn't allow me to track who initiated the relationship and thus distinguish between pending and requested relationships. What is the correct way to model this?
I've been playing around with a polymorphic attribute on the relationship model, but without concrete success.
you can try to use amistad gem
very good for friendship relationship and has a very good documentation that can help you out alot. just check it out
I'm using some open gov data in MYSQL which I've imported into my rails App.
I've extended the database with some of my own tables, and use the rails provided column ids in my tables.
However, the tables of the original database are linked through a unique 'ndb' id.
I thought that by cross-referencing two :foreign_key=>'ndb' between the models I would get the tables linked properly, but through :foreign_key, it appears to be linking the id from one table to the ndb column of another.
My models look like this
class Food < ActiveRecord::Base
has_many :weights, :foreign_key=>'ndb'
has_many :food_names
end
class Weight < ActiveRecord::Base
belongs_to :food, :foreign_key=>'ndb'
Is there a way to specify that the 'ndb' column is the link between the food and weights table, and not the food_id to ndb?
Have you tried set_primary_key 'ndb' in your AR classes?
set_primary_key docs.
What I've done as a possibly temporary solution, is to used the :finder_sql to link the tables together.
My Food model now has:
class Food < ActiveRecord::Base
has_many :weights, :finder_sql =>'Select weights.* FROM weights LEFT JOIN foods ON foods.ndb=weights.ndb WHERE foods.id=#{id}'
I'm not sure if this is the best solution or not, but it seems to be working.