Creating many, many relationships in Rails

This is a simplified example of what I'm trying to achieve, I'm relatively new to Rails and struggling to figure out the relationships between the models.

I have two models: the User model and the Category model. A user can be associated with many categories. A specific category may appear in the list of categories for many users. If a certain category is deleted, this should be reflected in the list of categories for the user.

In this example:

My Categories table contains five categories:

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  ID |  Name |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  1 |  Sports | 
 |  2 |  News |
 |  3 |  Entertainment |
 |  4 |  Technology |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

My Users table contains two users:

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  ID |  Name |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  1 |  UserA | 
 |  2 |  UserB |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The user can select Sports and Technology as their categories

User can select News, Sports and Entertainment

The sport category is deleted, and the UserA and UserB category lists reflect the deletion

I played with creating a UserCategories table that contains the identifiers of both the category and the user. This view worked, I could search for category names, but I could not get cascading deletion to work, and the whole solution just seemed wrong.

The examples of using the belongs_to and has_many functions that I found seem to discuss mapping a one-to-one relationship. For example, comments on a blog post.

  • How do you imagine this many-to-many relationship using the built-in Rails function?
  • Does it use a separate table between two viable solutions when using Rails?
+58
ruby-on-rails
Feb 25 2018-11-17T00:
source share
3 answers

You want the has_and_belongs_to_many relationship. The guide perfectly describes how this works with charts and everything else:

http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association

You will get something like this:

 # app/models/category.rb class Category < ActiveRecord::Base has_and_belongs_to_many :users end # app/models/user.rb class User < ActiveRecord::Base has_and_belongs_to_many :categories end 

Now you need to create a connection table to use Rails. Rails will not do this automatically for you. In fact, this is a table with a link to each of the categories and users, without a primary key.

Create a migration from the CLI as follows:

 bin/rails g migration CreateCategoriesUsersJoinTable 

Then open it and edit it so that it matches:

For Rails 4.0. 2+ (including Rails 5.2):

 def change # This is enough; you don't need to worry about order create_join_table :categories, :users # If you want to add an index for faster querying through this join: create_join_table :categories, :users do |t| t.index :category_id t.index :user_id end end 

Rails <4.0.2:

 def self.up # Model names in alphabetical order (eg a_b) create_table :categories_users, :id => false do |t| t.integer :category_id t.integer :user_id end add_index :categories_users, [:category_id, :user_id] end def self.down drop_table :categories_users end 

With this in mind, start the migration, and you can associate Categories and users with all the convenient means of access that you are used to:

 User.categories #=> [<Category @name="Sports">, ...] Category.users #=> [<User @name="UserA">, ...] User.categories.empty? 
+132
Feb 25 '11 at 17:48
source share

Just adding the Coreyward answer above: If you already have a model that has belongs_to , has_many and you want to create a new has_and_belongs_to_many relation using the same table, you will need:

 rails g migration CreateJoinTableUsersCategories users categories 

Then,

 rake db:migrate 

After that, you will need to determine your relationship:

User.rb:

 class Region < ApplicationRecord has_and_belongs_to_many :categories end 

Category.rb

 class Facility < ApplicationRecord has_and_belongs_to_many :users end 

In order to populate a new connection table with old data, you need to in the console:

 User.all.find_each do |u| Category.where(user_id: u.id).find_each do |c| u.categories << c end end 

You can leave user_id and category_id from the Category and User tables or create a migration to delete them.

0
Jan 21 '19 at 22:10
source share

The most popular is the "Monotransitive Association", you can do this:

 class Book < ApplicationRecord has_many :book_author has_many :author, through: :book_author end class BookAuthor < ApplicationRecord belongs_to :book belongs_to :author end class Author < ApplicationRecord has_many :book_author has_many :book, through: :book_author end 

The has_many: through association is often used to establish a many-to-many relationship with another model. This relationship indicates that the declaring model can be matched to zero or more instances of another model by going through the third model. For example, consider medical practice in which patients make an appointment with a doctor. Link: https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

0
Apr 10 '19 at 12:08 on
source share



All Articles