Ruby-on-rails 在 Rails 中创建多对多关系

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5120703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:30:50  来源:igfitidea点击:

Creating a many to many relationship in Rails

ruby-on-rails

提问by fletcher

This is a simplified example of what I am trying to achieve, I'm relatively new to Rails and am struggling to get my head around relationships between models.

这是我想要实现的目标的简化示例,我对 Rails 比较陌生,并且正在努力了解模型之间的关系。

I have two models, the Usermodel and the Categorymodel. A user can be associated with many categories. A particular category can appear in the category list for many users. If a particular category is deleted, this should be reflected in the category list for a user.

我有两个模型,User模型和Category模型。一个用户可以与许多类别相关联。特定类别可以出现在许多用户的类别列表中。如果特定类别被删除,这应该反映在用户的类别列表中。

In this example:

在这个例子中:

My Categoriestable contains five categories:

我的Categories表包含五个类别:

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

My Userstable contains two users:

我的Users表包含两个用户:

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

UserA may choose Sports and Technology as his categories

UserB may choose News, Sports and Entertainment

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

UserA 可以选择 Sports 和 Technology 作为他的类别

用户 B 可以选择新闻、体育和娱乐

运动类别被删除,UserA和UserB类别列表都反映了删除

I've toyed around with creating a UserCategoriestable which holds the ids of both a category and user. This kind of worked, I could look up the category names but I couldn't get a cascading delete to work and the whole solution just seemed wrong.

我一直在尝试创建一个UserCategories包含类别和用户 ID 的表。这种工作,我可以查找类别名称,但我无法让级联删除工作,整个解决方案似乎是错误的。

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

我发现的使用belongs_to 和has_many 函数的示例似乎讨论了映射一对一关系。例如,对博客文章的评论。

  • How do you represent this many-to-many relationship using the built-in Rails functionality?
  • Is using a separate table between the two a viable solution when using Rails?
  • 您如何使用内置的 Rails 功能来表示这种多对多关系?
  • 使用 Rails 时,在两者之间使用单独的表是一个可行的解决方案吗?

回答by coreyward

You want a has_and_belongs_to_manyrelationship. The guide does a great job of describing how this works with charts and everything:

你想要一段has_and_belongs_to_many关系。该指南很好地描述了如何处理图表和所有内容:

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

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

You will end up with 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 join table for Rails to use. Rails will not do this automatically for you. This is effectively a table with a reference to each of Categories and Users, and no primary key.

现在您需要创建一个供 Rails 使用的连接表。Rails 不会自动为您执行此操作。这实际上是一个表,其中包含对每个类别和用户的引用,并且没有主键。

Generate a migration from the CLI like this:

从 CLI 生成迁移,如下所示:

bin/rails g migration CreateCategoriesUsersJoinTable

Then open it up and edit it to match:

然后打开它并编辑它以匹配:

For Rails 4.0.2+ (including Rails 5.2):

对于 Rails 4.0.2+(包括 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:

导轨 < 4.0.2:

def self.up
  # Model names in alphabetical order (e.g. 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 that in place, run your migrations and you can connect Categories and Users with all of the convenient accessors you're used to:

有了它,运行您的迁移,您就可以将类别和用户与您习惯的所有方便的访问器连接起来:

User.categories  #=> [<Category @name="Sports">, ...]
Category.users   #=> [<User @name="UserA">, ...]
User.categories.empty?

回答by Darlan Dieterich

The most popular is 'Mono-transitive Association', you can do this:

最受欢迎的是“单传递关联”,你可以这样做:

class Book < ApplicationRecord
  has_many :book_authors
  has_many :authors, through: :book_authors
end

# in between
class BookAuthor < ApplicationRecord
  belongs_to :book
  belongs_to :author
end

class Author < ApplicationRecord
  has_many :book_authors
  has_many :books, through: :book_authors
end

A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model. For example, consider a medical practice where patients make appointments to see physicians. Ref.: https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

has_many :through 关联通常用于与另一个模型建立多对多连接。这种关联表明声明模型可以通过第三个模型与另一个模型的零个或多个实例相匹配。例如,考虑患者预约看医生的医疗实践。参考:https: //guides.rubyonrails.org/association_basics.html#the-has-many-through-association

回答by B-M

Just complementing coreyward's answer above: If you already have a model that has a belongs_to, has_manyrelation and you want to create a new relation has_and_belongs_to_manyusing the same table you will need to:

只是补充上面 coreyward 的回答:如果您已经有一个具有belongs_to,has_many关系的模型,并且您想has_and_belongs_to_many使用同一个表创建一个新关系,您将需要:

rails g migration CreateJoinTableUsersCategories users categories

Then,

然后,

rake db:migrate

After that, you will need to define your relations:

之后,您需要定义您的关系:

User.rb:

用户.rb:

class Region < ApplicationRecord
  has_and_belongs_to_many :categories
end

Category.rb

类别.rb

class Facility < ApplicationRecord
  has_and_belongs_to_many :users
end

In order to populate the new join table with the old data, you will need to in your console:

为了用旧数据填充新的连接表,您需要在控制台中:

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

You can either leave the user_idcolumn and category_idcolumn from the Category and User tables or create a migration to delete it.

您可以保留Category 和 User 表中的user_id列和category_id列,也可以创建迁移以将其删除。