如何在 has_and_belongs_to_many 关系中设置 rails 模型?

How do I set up the rails models in a has_and_belongs_to_many relationship?

我正在开发博客 rails 应用程序。我想让每个 post 都有很多标签,并且能够显示属于给定标签的所有 post。在 Rails Guide and I also tried this tutorial.

之后,我已经完成了 has_and_belongs_to_many 关系的设置

两次我都在设置模型后进入 rails 控制台,检查 table 的格式是否正确。我调用了 Post.newTag.new。每个都在 table 中给我一个新的 instance/entry,但都没有显示用于引用另一个的列。不应该有一个吗?如果没有,我应该如何构建我的种子数据?

到目前为止,我只在处理 belongs_to 和 has_many 关系。如果有人在 has_and_belongs_to_many 上有 link 到更好的 explanation/tutorial,我也将不胜感激,我的搜索结果还没有很清楚。

这是我的代码:

post.rb:

class Post < ActiveRecord::Base
  has_and_belongs_to_many :tags
  belongs_to :categories
end

tag.rb:

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :posts
end

longnumber_create_posts.rb:

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :name
      t.string :content
      t.datetime :pub_time
      t.integer :category_id
      t.timestamps null: false
    end
  end
end

lognumber_create_tags.rb:

class CreateTags < ActiveRecord::Migration
  def change
    create_table :tags do |t|
      t.string :name
      t.timestamps null: false
    end
  end
end

longnumber_create_posts_and_tags.rb:

class CreatePostsAndTags < ActiveRecord::Migration
  def change
    create_table :posts_tags, id: false do |t|
      t.belongs_to :post, index: true
      t.belongs_to :tag, index: true
    end
  end
end

schema.rb:

ActiveRecord::Schema.define(version: 20151123235501) do

  create_table "posts", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "posts_tags", id: false, force: :cascade do |t|
    t.integer "post_id"
    t.integer "tag_id"
  end

  add_index "posts_tags", ["post_id"], name: "index_posts_tags_on_post_id"
  add_index "posts_tags", ["tag_id"], name: "index_posts_tags_on_tag_id"

  create_table "tags", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

您需要在 Tag and/or Post 上调用 create,否则记录不会存储在数据库中。

下面您将创建一个 post 和一个与之相关的标签 post。

post = Post.create!(content: "my content")
post.tags.create!(name: "movies")

您可以将 TagPost 视为数组。例如:

post = Post.create!(title: 'title')

tag = Tag.create!(name: 'test')

tag.posts << post

您已经分别实例化了标签和 Post,但需要让一个对象了解另一个对象。此关联是在您创建的联接 table 中建立的。您可以通过几种方式建立这些关联:

选项 1

Post.create(name: text) # assuming your db was empty, this post now has id = 1
Tag.create(name: text, post_id: 1)

选项 2

post = Post.create(name: text)
tag = Tag.create(name: text)

# associate tag with post
post.tags << tag

选项 2 可能更可取,通常可以在您的控制器中调用。例如:

# in PostsController
def create
  post = Post.create(post_params)
  tag = Tag.find_or_create_by(params[:tag_id])
  post.tags << tag

  render root_path
end

private

  def post_params
    params.require(:post).permit(:name, :content, :pub_time, :category_id, :tag_id)
  end

在多对多关系中,使用您的示例,您不会在 Post 或标记 table 中看到外键,因为它使用的是 "join" table 您创建了名为 Post 的标签来存储关系。由于您已经在 3 个模型(Post、Tag、PostTag)中设置了 has_many 和 belongs_to 关联,因此您现在必须告诉 Rails在这些 post 和标签之间创建关联。这是按如下方式完成的:

@post = Post.new
@tag = Tag.last (The last is just an example, make sure to use the instance you need!

@post.tags << @tag   #This is where the magic happens and the association is formed

最近大多数 Rails 社区已经转向使用 "has_many through" 方法,因为它允许将额外的属性添加到连接 table 而在这里你只能存储外键在 Post 标签 table.

更新:根据以下关于此代码去向的评论:

通常这会在您的控制器中执行,但您也可以在 rails 控制台中执行此操作。例如,如果您有一些 post 的表单输入,并在提交表单时添加了一个标签 ID 来关联它,您的控制器很可能看起来像这样:

def create
@post = Post.create(post_params)
if @post.save
@tag = Tag.find(params[:tag_id])
@post.tags << @tag
redirect_to some_fun_path
else
render 'new'
end


private

def post_params
   params.require(:post).permit(:tag_id, :title, :description, etc.)
end