将表拆分为两个相关联的表



我有一个有博客功能的应用程序。最初,当我设置这个帖子,帖子由标题,正文,关键字和图像链接组成。我希望能够过滤基于关键字的帖子,我认为最干净的方式来做到这一点是移动关键字到自己的表和关联的两个表。所以每篇文章可以有多个关键字,每个关键字可以有多个文章。

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

class CreateKeywords < ActiveRecord::Migration[6.1]
def change
create_table :keywords do |t|
t.string :keyword
t.timestamps
end
end
end
class Keyword < ApplicationRecord
has_and_belongs_to_many :posts
end

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

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

def post_params
params.require(:post).permit(:title, :body, :image, :keywords_attributes => [:id, :keyword])
end

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

params = { post: {title: post_params["title"], body: post_params["body"], image: post_params["image"], keywords_attributes: [{ keyword: 'keyword title' },]
}}

在上述状态下,当我到达这部分post = post .new(post_params)时,我得到一个错误说NoMethodError(未定义的方法'each' for "authorlife":String)。如果我从post_params中删除关键字,我会得到以下错误:unallowed parameter::keywords

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

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 = { 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表移动到关键字表。像这样

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

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

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

最新更新