我有一个名为Document
的ActiveRecord模型,并围绕它实现了CRUD操作。我只是在验证失败时在请求之间持久存在Document
实例时遇到问题(因为我想在发生这种情况时重定向到另一个页面)。
首先,我尝试将实例存储在flash会话中:
# documents_controller.rb
def new
@document = flash[:document] || Document.new
end
def create
document = Document.new(document_params)
if document.save
return redirect_to documents_path
end
flash[:document] = document
redirect_to new_document_path
end
使用上面的代码,我希望实际的Document
实例存储在flash会话中,但它变成了一个看起来像#<Document:0xad32368>
的字符串。在线搜索了一段时间后,我发现由于某些原因你无法在会话中存储ActiveRecord对象。
关于将对象的id
存储在flash会话中有很多建议,但我不能这样做,因为正如您所看到的,该对象尚未存储在数据库中。
接下来,我尝试在重定向之后重构Document
实例,利用实例的attributes
方法(返回可存储在会话中的可序列化哈希):
# documents_controller.rb
def new
@document = Document.new(flash[:document_hash] || {})
end
def create
...
flash[:document_attributes] = document.attributes
redirect_to new_document_path
end
这几乎解决了这个问题,但不保留验证错误(document.errors
)的部分除外。此外,如果这用于保存已存储在数据库中的实例(在更新Document
实例时验证失败的情况下),我不确定原始属性和新属性之间的哪个将被保留
现在我已经想出了尝试的想法。谁有这个合适的解决方案?
修改
您可能想知道为什么我仍然需要重定向到另一个页面而不是仅仅在new
方法中呈现新文档视图模板或create
操作。我这样做是因为我的视图中有一些东西依赖于当前的控制器方法。例如,当您在文档创建页面上时,我有一个需要突出显示的选项卡(通过检查action_name == "new" and controller_name == "documents"
来完成)。如果我这样做:
def create
...
render action: "new"
end
标签不会突出显示,因为action_name
现在为create
。我也不能只添加其他条件来突出显示标签action_name == "create"
,因为文档也可以从索引页面(documents_path
)创建。文档也可以从索引页面(documents_path
)或详细信息页面(document_path(document)
)更新,如果验证在update
方法中失败,我想重定向到上一页。
答案 0 :(得分:2)
如果我真的需要在请求之间伪造某些东西(你设置的所有变量在请求之间丢失),我将通常将相关属性放入新表单中的隐藏字段中。
在你的情况下,这是矫枉过正。在您的代码中,您正在重定向,这会导致新的请求:
def create
document = Document.new(document_params)
if document.save
return redirect_to documents_path
end
flash[:document] = document
redirect_to new_document_path
end
您可以使用render action: 'action_to_render'
轻松渲染其他操作的输出,而不是重定向。所以在你的例子中,这可能是:
def create
@document = Document.new(document_params)
if @document.save
render action: 'index'
else
render action: 'new'
end
end
可以简化为:
def create
@document = Document.new(document_params)
action_to_render = @document.save ? 'index' : 'new'
render action_to_render
end
如果您需要操作中的额外逻辑,则可以将逻辑重构为从两个操作调用的方法,或简单地call the other action from the current one。
偶尔会很好,但我要提醒的是,不得不过多地渲染渲染,这通常表明架构很差。
修改强>
给定新突出显示的约束,另一个选项可能是使new和create方法相同。删除new
操作和路由,并为GET和PATCH请求创建答案。该操作可能类似于:
def create
@document = Document.new(document_params)
request.patch? && @document.save && redirect_to( documents_path )
end
我实际上对几乎所有的控制器都使用了与此类似的东西,因为它会显着地干掉事物(因为你可以删除额外的可能相同的视图)
另一种选择是只使用一个实例变量来跟踪此实例中的活动选项卡,并使其余代码更清晰。
答案 1 :(得分:0)
<强>解决强>
我能够使用ActiveSupport::Cache::Store
为其制作解决方法(正如@AntiFun所建议的那样)。首先,我创建了一个fake_flash
方法,它与flash会话非常相似,只是它使用缓存来存储数据,它看起来像这样:
def fake_flash(key, value)
if value
Rails.cache.write key, value
else
object = Rails.cache.read key
Rails.cache.delete key
object
end
end
然后我就像flash会话一样使用它。
# documents_controller.rb
def new
...
@document = fake_flash[:document] || Document.new
...
end
def create
document = Document.new document_params
...
# if validation fails
fake_flash :document, document
redirect_to new_document_page
end