我有一个模型项目和一个模型测试。 项目有很多测试。
我的tests_controller
有:
def new
@project = Project.find(params[:project_id])
@test = @project.tests.new
end
def create
@project = Project.find(params[:project_id])
@test = @project.tests.create(test_params)
end
所以在我的new.html.erb
中,我提出了一个调用new
的表单,当按下提交时,调用create
。然而,我很困惑,因为显然正在发生的事情是我正在new
创建一个测试,在create
中创建完全不同的测试。我想要实现的目标的正确范式是什么?
我在new
中创建新测试的原因是,我希望我的创建表单知道要创建的测试对象的 id。
create
操作应如下所示:
def create
@project = Project.find(params[:project_id])
@test = @project.test.create(params[:test])
if @test.save
redirect_to SOME_path, flash: { success: "Test added successfully" }
else
flash.now[:error] = "Something's gone wrong. Please try again!"
render 'new'
end
end
为什么 Rails 不将对象持久化在#new
控制器操作中?
-
HTTP 请求一致性
所有
#new
操作都映射到 GET 请求,不应使用该请求创建资源。创建资源可以通过两种方式完成:非幂等方式,通过使用映射到 POST 的#create
;以及幂等方式,通过使用映射到 PUT(或在某些情况下为 PATCH)的#update
。当您想要创建您已经知道其 id 的资源时,后者是要走的路。 -
验证
验证背后的整个想法是,它们应该在持久化数据之前应用。因此,在
#new
操作中,我们通常将自己限制为实例化模型类,然后在#create
操作中,我们至少可以有两个可能的响应 - 一个是清除验证并持久化对象时成功的响应,另一个是当我们在持久化对象时出现错误时失败的响应。 -
使数据库没有空对象
#new
和#create
是不同操作的另一个原因是服务器可能会丢失与它们之间的客户端的连接。而且,当这种情况发生时,您不希望数据库中有一个空对象,因为第二个操作从未通过。
也就是说,如果你仍然有充分的理由在请求用户输入之前保留对象,并且你不需要验证,并且你不介意数据库中有空对象,你可以通过完全跳过#new
操作来实现,有点像这样:
def create
@project = Project.find(params[:project_id])
@test = @project.tests.create
end
def update
@project = Project.find(params[:project_id])
@test = @project.tests.update(test_params)
end