Creando una relación de muchos a muchos en Rails

Este es un ejemplo simplificado de lo que estoy tratando de lograr, soy relativamente nuevo en Rails y estoy luchando para entender las relaciones entre modelos.

Tengo dos modelos, el modelo de User y el modelo de Category . Un usuario puede asociarse con muchas categorías. Una categoría particular puede aparecer en la lista de categorías para muchos usuarios. Si se elimina una categoría en particular, esto debería reflejarse en la lista de categorías para un usuario.

En este ejemplo:

Mi tabla de Categories contiene cinco categorías:

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  ID |  Nombre |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  1 |  Deportes | 
 |  2 |  Noticias |
 |  3 |  Entretenimiento |
 |  4 |  Tecnología |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Mi tabla de Users contiene dos usuarios:

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  ID |  Nombre |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 |  1 |  UserA | 
 |  2 |  UsuarioB |
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

UsuarioA puede elegir Deportes y Tecnología como sus categorías

El usuario B puede elegir Noticias, Deportes y Entretenimiento

La categoría deportiva se elimina, las listas de categorías UserA y UserB reflejan la eliminación

He jugado con crear una tabla UserCategories que contenga los ID de una categoría y un usuario. Este tipo de trabajo, pude buscar los nombres de las categorías, pero no pude hacer que funcionara una eliminación en cascada y toda la solución me pareció incorrecta.

Los ejemplos del uso de las funciones belongs_to y has_many que he encontrado parecen discutir el mapeo de una relación de uno a uno. Por ejemplo, comentarios en una publicación de blog.

  • ¿Cómo se representa esta relación de muchos a muchos usando la funcionalidad incorporada de Rails?
  • ¿Usar una tabla separada entre las dos es una solución viable cuando se usa Rails?

Quieres una relación has_and_belongs_to_many . La guía hace un excelente trabajo al describir cómo funciona esto con gráficos y todo:

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

Terminarás con algo como esto:

 # 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 

Ahora necesita crear una tabla de unión para que Rails la use. Rails no hará esto automáticamente por ti. Esto es efectivamente una tabla con una referencia a cada una de las Categorías y Usuarios, y no tiene una clave principal.

Genere una migración desde la CLI de esta manera:

 bin/rails g migration CreateCategoriesUsersJoinTable` 

A continuación, ábralo y edítelo para que coincida:

Para Rails 4.0.2+ (incluido Rails 5.1):

 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 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 

Con eso en su lugar, ejecute sus migraciones y puede conectar Categorías y Usuarios con todos los accesos convenientes a los que está acostumbrado:

 User.categories #=> [, ...] Category.users #=> [, ...] User.categories.empty?