首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将表拆分为两个关联表

将表拆分为两个关联表
EN

Stack Overflow用户
提问于 2021-08-27 00:35:22
回答 1查看 46关注 0票数 0

我有一个有博客功能的应用程序。最初,当我设置这个帖子时,帖子由标题、正文、关键字和图像链接组成。我希望能够根据关键字过滤帖子,我认为最干净的方法是将关键字移动到自己的表中,并将这两个表关联起来。因此每个帖子可以有多个关键字,每个关键字可以有多个帖子。

我为关键字创建了一个迁移和模型。

代码语言:javascript
运行
复制
class CreateKeywords < ActiveRecord::Migration[6.1]
  def change
    create_table :keywords do |t|
      t.string :keyword

      t.timestamps
    end
  end
end
代码语言:javascript
运行
复制
class Keyword < ApplicationRecord
  has_and_belongs_to_many :posts
end

我将其与posts表关联,并更改了posts模型。

代码语言:javascript
运行
复制
class CreatePostsKeywordsJoinTable < ActiveRecord::Migration[6.1]
  def change
    create_join_table :posts, :keywords do |t|
      t.index [:post_id, :keyword_id]
      t.index [:keyword_id, :post_id]
    end
  end
end
代码语言:javascript
运行
复制
class Post < ApplicationRecord
  belongs_to :user
  has_and_belongs_to_many :keywords

  validates :title, presence: true
  validates :body, presence: true

  accepts_nested_attributes_for :keywords
  # validates :keywords, presence: true
  
end

现在,我只注释掉了Post模型中的关键字。我不太确定是否需要删除它。我已经有了一些现有的帖子,我不想因为这次转换而失去这些帖子,所以我试着在弄清楚如何让这个帖子生效的时候把它放在心上。我真正感到困惑的是我需要在控制器中更改什么。

这是我的Post控制器:

代码语言:javascript
运行
复制
require 'pry'

class Api::V1::PostController < ApiController
  before_action :authorize_user, except: [:index, :show]

  # INDEX /post
  def index
    render json: Post.all, each_serializer: PostSerializer
  end

  # SHOW /post/1
  def show
    render json: Post.find(params[:id]), serializer: PostShowSerializer
  end

  # CREATE /post/new
  def create
    binding.pry
    post = Post.new(post_params)
    post.user = current_user

    if post.save
      render json: post
    else
      render json: { errors: post.errors.full_messages }
    end 

  end

  # UPDATE /post/update
  def update
    post = Post.find(params[:id])

    if post.update(post_params)
      render json: post
    else
      render json: { errors: post.errors.full_messages }
    end
  end

  # DESTROY /post/destroy
  def destroy
    post = Post.find(params[:id])
  
    if post.destroy
      render json: {destroyed: true}
    end
    
  end
  
  protected

  def post_params
    params.require(:post).permit([:title, :body, :image, :keywords])
  end

  def authorize_user
    if !user_signed_in? || current_user.role != "admin"
      redirect_to root_path
    end
  end

end

在上面的状态中,当我到达这个部分post = Post.new(post_params)时,我得到一个错误,说是NoMethodError (undefined method 'each' for "authorlife":String)。如果从post_params中删除keywords,则会出现以下错误Unpermitted parameter: :keywords

我觉得我在这里遗漏了一个或多个步骤,但我已经有一段时间没有做过像这样的关联表了。

更新:遵循下面的一些建议,我更新了上面的代码,使其看起来像现在的样子。当前的问题是,当我在#create方法中检查post_parms时,我根本不再接收到关键字。我检查了前台,它正在发送关键字。我假设是我的post_params导致了这个问题。我尝试过像这样添加关键字嵌套属性,但是关键字仍然没有显示在post_params

代码语言:javascript
运行
复制
def post_params
    params.require(:post).permit(:title, :body, :image, :keywords_attributes => [:id, :keyword])
  end

这是我试图实现的代码的WIP。一旦我弄清楚了参数的情况,我不确定关键字部分应该是什么样子。

代码语言:javascript
运行
复制
params = { post: {title: post_params["title"], body: post_params["body"], image: post_params["image"], keywords_attributes: [{ keyword: 'keyword title' },]
}}
EN

回答 1

Stack Overflow用户

发布于 2021-08-27 01:13:37

在上面的状态中,当我到达这个部分

= Post.new(post_params)时,我得到一个错误,说NoMethodError (未定义的方法‘’for "authorlife":String)。如果我从post_params中删除关键字,我会得到这个错误::关键字参数不允许

如果要通过post更新关键字,则需要设置关键字的嵌套属性。

代码语言:javascript
运行
复制
class Post < ApplicationRecord
  belongs_to :user
  has_and_belongs_to_many :keywords

  validates :title, presence: true
  validates :body, presence: true

  accepts_nested_attributes_for :keywords
end

然后,您可以在您的控制器中传入这样结构的params

代码语言:javascript
运行
复制
params = { post: {
  title: 'title', body: "body", keywords_attributes: [
    { text: 'keyword title' },
  ]
}}

post = Post.create(params[:post])

https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

我已经有了一些现有的帖子,我不想因为这次转换而失去这些帖子,所以我正在努力保持这一点,因为我想知道如何让它发挥作用。

删除不再使用的数据是一种很好的做法。您应该编写一个数据迁移,将现有的关键字从posts表移动到keywords表。像这样的东西

代码语言:javascript
运行
复制
class KeywordsMigrator
  def run
    Post.all.each do |post|
      keyword = Keyword.find_or_create_by(title: post.keyword)
      post.keywords << keyword
    end
  end
end

最后,您可以从post中删除keyword列。

您并没有真正提到posts表的当前结构,所以我假设您在那里有一个keyword列。如果您有一个keywords列,那么您必须对您的关联进行不同的命名,直到您删除该列为止,否则您将遇到麻烦。例如,将其重命名为keywords_v1并指定class_name

代码语言:javascript
运行
复制
class Post < ApplicationRecord
  belongs_to :user
  has_and_belongs_to_many :keywords_v1, class_name: "Keyword"

  validates :title, presence: true
  validates :body, presence: true  
end

或者先将该列重命名为类似于deprecated_keywords的名称。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68946768

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档