我的问题是:为什么.becomes
不传递错误到新对象?这不就是预期的行为吗?
我在rails应用程序中有以下单表继承类:
class Document < ActiveRecord::Base
validates :title, :presence => true
end
class LegalDocument < Document
end
class MarketingDocument < Document
end
我想使用相同的控制器和视图集来编辑LegalDocument
s和MarketingDocument
s,所以我使用DocumentsController < ApplicationController
与以下edit
和update
动作:
def edit
@document = Document.find(params[:id])
end
def update
@document = Document.find(params[:id])
if @document.update_attributes(params[:document])
redirect_to documents_path, :notice => "#{t(:Document)} was successfully updated."
else
render :action => "edit"
end
end
和以下在我的edit
视图:
<%= form_for @document.becomes(Document) do |f| %>
<% if f.object.errors.present? %>
<div class="error_message">
<h4><%= pluralize(f.object.errors.count, 'error') %> occurred</h4>
</div>
<% end %>
<div>
<%= f.label :title %>
<%= f.text_field :title, :class => "inputText" %>
</div>
<%= f.submit %>
<% end %>
- 如果填了title,说明文档更新正确。
- 如果标题为空,我返回到编辑视图,但不显示错误。
从调试,我知道它没有显示,因为f.object.errors
是nil。然而,从调试中,我也知道@document.errors
不是nil,正如预期的那样。
我的问题是:为什么.becomes
不传递错误到新的对象?这不就是预期的行为吗?
是的,我也注意到了。
把f.object.errors.present?
改成@document.errors.any?
(或@document.errors.present?
)。
如果你真的想使用f.object.errors.present?
,将becomes
写入控制器(编辑和更新操作),而不是在视图中:
def edit
@document = Document.find(params[:id]).becomes(Document)
end
def update
@document = Document.find(params[:id]).becomes(Document)
# ....
end
然后在视图中:
<%= form_for @document do |f| %>
<% if f.object.errors.present? %>
<p>Errrorsss....</p>
<% end %>
#.....
这是因为表单的url是根据@document. gets (Document) (=> PUT document/:id
)创建的,而@document是根据它的"true"类(Document的子类)创建的。
如果你有撬(强烈建议),写:
def update
@document = Document.find(params[:id])
binding.pry
# ...
end
然后检查@document。你会看到@document是LegalDocument或其他子类的一个实例,即使你在表单中调用了@document.becomes(Document)
。
所以最后的f.object
和@document
是不一样的
这解释了为什么当验证失败时看不到f.object.errors
。
编辑
处理STI和form的"最佳方法"是不使用becomes
:
<= form_for @document, url: { controller: 'documents', action: 'update' }, as: :document do |f| %>
<% if @document.errors.any? %>
# or if f.object.errors.any?
# handle validation errors
<% end %>
# your form...
<% end %>
只有一个控制器(documents_controller)
只有一个资源(资源:文档)
它跟踪你的子类:LegalDocument将被存储为LegalDocument。不转换:您不必在转换为Document之前存储它的类,然后在稍后重新分配它。另外,您的子类在表单中可用,因此您可以(让我们想象一下)为类型构建一个
select
。您的控制器看起来更干净:
@document = Document.find params[:id]
仅此而已。就像一个经典的资源。
如果您想在不同的操作(通常是edit
和new
)之间共享此表单:
<%= form_for @document, url: { controller: 'media_files', action: action }, as: :media_file do |f| %>%>
# edit.html.erb
<%= render 'form', action: 'update' %>
# new.html.erb
<%= render 'form', action: 'create' %>
这基本上是一个bug,它应该像您最初期望的那样工作。下面的补丁解决了这个问题,看起来它是在10月份被拉回来的
https://github.com/lazyatom/rails/commit/73cb0f98289923c8fa0287bf1cc8857664078d43