如何在不使用嵌套参数的情况下在 Rails 中同时保存父对象和子对象?
How can I save parent and child objects at the same time in Rails without using nested parameters?
我在 Rails 6 应用程序中有具有父子关系的对象:
class Sequence < ApplicationRecord
has_many :frames, dependent: :destroy, autosave: true
belongs_to :sequence_list
end
class Frame < ApplicationRecord
belongs_to :sequence
end
在sequences_controller.rb中,我想在同一个控制器动作中创建一个序列和一堆子帧。
这是 sequences_controller 创建操作:
# POST /sequences
# POST /sequences.json
def create
#@sequence = Sequence.new(sequence_params)
@sequence = Sequence.new
@sequence.name = params[:sequence][:name]
filename = params[:sequence][:upload][:file].original_filename
tmp_filename = params[:sequence][:upload][:file].tempfile
if (File.extname(filename)==".txt")
Frame.transaction do
File.readlines(tmp_filename).each do |line|
i = Frame.new
i.url = line.strip
i.sequence = @sequence
i.save
end
end
end
tmp_filename.close
tmp_filename.unlink
respond_to do |format|
if @sequence.save
format.html { redirect_to @sequence, notice: 'Sequence was successfully created.' }
format.json { render :show, status: :created, location: @sequence }
else
format.html { render :new }
format.json { render json: @sequence.errors, status: :unprocessable_entity }
end
end
end
当我POST执行此操作时,出现以下错误:
SQLite3::ConstraintException: NOT NULL constraint failed: frames.sequence_id
错误来自 Frame.transaction
块中的行 i.save
。
我知道发生此错误是因为 i.sequence
未分配,因为 @sequence
尚未保存。
我相信在这种情况下的常见方法可能是使用嵌套参数一次性创建子对象,但在这种情况下,子对象的参数实际上在 params[]
哈希中不可用因为我没有通过 JSON 发送它们;我正在发送一个文本文件,该文件由该控制器操作解析以生成子对象列表。
我发送文本文件(而不是使用嵌套表单)的原因是因为子对象太多,通过浏览器手动创建它们会很乏味。每个父级有成百上千个子对象,因此解析上传似乎是显而易见的方法。
我想解决这个问题的一种方法是使用 Javascript 在前端解析文本文件并构建一个嵌套表单,但是是否可以从控制器解决这个问题?
我一直在阅读一些有关 :inverse_of
和 :autosave
的文章,但我不清楚这种情况下的默认方法是什么。我怎样才能同时保存父项和子项,同时允许像普通控制器创建方法一样在失败时回滚,而无需在前端解析文本文件?
您的问题是 Sequence
未保存,您将其作为数据库中的关系。
现在为了让您的代码正常工作,您 sequence
实例应该先于其他实例创建。
幸运的是,ActiveRecord
可以解决这个问题。这是你应该做的:
# POST /sequences
# POST /sequences.json
def create
#@sequence = Sequence.new(sequence_params)
@sequence = Sequence.new
@sequence.name = params[:sequence][:name]
filename = params[:sequence][:upload][:file].original_filename
tmp_filename = params[:sequence][:upload][:file].tempfile
if (File.extname(filename)==".txt")
Frame.transaction do
File.readlines(tmp_filename).each do |line|
@sequence.frames.new(url: line.strip)
end
end
end
tmp_filename.close
tmp_filename.unlink
respond_to do |format|
if @sequence.save
format.html { redirect_to @sequence, notice: 'Sequence was successfully created.' }
format.json { render :show, status: :created, location: @sequence }
else
format.html { render :new }
format.json { render json: @sequence.errors, status: :unprocessable_entity }
end
end
end
我所做的是初始化序列的帧,ActiveRecord
将在您保存 @sequence
模型时处理它们的创建。
注意:您的 Sequence
模型当然应该有 has_many :frames
。
你可以使用这个:
class Frame < ApplicationRecord
belongs_to :sequence, optional: true
end
我在 Rails 6 应用程序中有具有父子关系的对象:
class Sequence < ApplicationRecord
has_many :frames, dependent: :destroy, autosave: true
belongs_to :sequence_list
end
class Frame < ApplicationRecord
belongs_to :sequence
end
在sequences_controller.rb中,我想在同一个控制器动作中创建一个序列和一堆子帧。
这是 sequences_controller 创建操作:
# POST /sequences
# POST /sequences.json
def create
#@sequence = Sequence.new(sequence_params)
@sequence = Sequence.new
@sequence.name = params[:sequence][:name]
filename = params[:sequence][:upload][:file].original_filename
tmp_filename = params[:sequence][:upload][:file].tempfile
if (File.extname(filename)==".txt")
Frame.transaction do
File.readlines(tmp_filename).each do |line|
i = Frame.new
i.url = line.strip
i.sequence = @sequence
i.save
end
end
end
tmp_filename.close
tmp_filename.unlink
respond_to do |format|
if @sequence.save
format.html { redirect_to @sequence, notice: 'Sequence was successfully created.' }
format.json { render :show, status: :created, location: @sequence }
else
format.html { render :new }
format.json { render json: @sequence.errors, status: :unprocessable_entity }
end
end
end
当我POST执行此操作时,出现以下错误:
SQLite3::ConstraintException: NOT NULL constraint failed: frames.sequence_id
错误来自 Frame.transaction
块中的行 i.save
。
我知道发生此错误是因为 i.sequence
未分配,因为 @sequence
尚未保存。
我相信在这种情况下的常见方法可能是使用嵌套参数一次性创建子对象,但在这种情况下,子对象的参数实际上在 params[]
哈希中不可用因为我没有通过 JSON 发送它们;我正在发送一个文本文件,该文件由该控制器操作解析以生成子对象列表。
我发送文本文件(而不是使用嵌套表单)的原因是因为子对象太多,通过浏览器手动创建它们会很乏味。每个父级有成百上千个子对象,因此解析上传似乎是显而易见的方法。
我想解决这个问题的一种方法是使用 Javascript 在前端解析文本文件并构建一个嵌套表单,但是是否可以从控制器解决这个问题?
我一直在阅读一些有关 :inverse_of
和 :autosave
的文章,但我不清楚这种情况下的默认方法是什么。我怎样才能同时保存父项和子项,同时允许像普通控制器创建方法一样在失败时回滚,而无需在前端解析文本文件?
您的问题是 Sequence
未保存,您将其作为数据库中的关系。
现在为了让您的代码正常工作,您 sequence
实例应该先于其他实例创建。
幸运的是,ActiveRecord
可以解决这个问题。这是你应该做的:
# POST /sequences
# POST /sequences.json
def create
#@sequence = Sequence.new(sequence_params)
@sequence = Sequence.new
@sequence.name = params[:sequence][:name]
filename = params[:sequence][:upload][:file].original_filename
tmp_filename = params[:sequence][:upload][:file].tempfile
if (File.extname(filename)==".txt")
Frame.transaction do
File.readlines(tmp_filename).each do |line|
@sequence.frames.new(url: line.strip)
end
end
end
tmp_filename.close
tmp_filename.unlink
respond_to do |format|
if @sequence.save
format.html { redirect_to @sequence, notice: 'Sequence was successfully created.' }
format.json { render :show, status: :created, location: @sequence }
else
format.html { render :new }
format.json { render json: @sequence.errors, status: :unprocessable_entity }
end
end
end
我所做的是初始化序列的帧,ActiveRecord
将在您保存 @sequence
模型时处理它们的创建。
注意:您的 Sequence
模型当然应该有 has_many :frames
。
你可以使用这个:
class Frame < ApplicationRecord
belongs_to :sequence, optional: true
end