Rails,STI和'变成' - f.object.errors未在视图中显示

时间:2011-12-08 16:27:36

标签: ruby-on-rails ruby single-table-inheritance form-for sti

我的问题是:为什么.becomes没有将错误传递给新对象?这不是预期的行为吗?


我在rails app中有以下单表继承类:

class Document < ActiveRecord::Base
  validates :title, :presence => true
end

class LegalDocument < Document
end

class MarketingDocument < Document
end

我想使用相同的控制器和一组视图来编辑LegalDocumentMarketingDocument,因此我使用DocumentsController < ApplicationController以及edit和{ {1}}行动:

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 %> 是零。 但是,从调试开始,我也知道f.object.errors并非正如预期的那样。


我的问题是:为什么@document.errors没有将错误传递给新对象?这不是预期的行为吗?

2 个答案:

答案 0 :(得分:2)

是的,我也注意到了。

只需按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.becomes(Document)(=&gt; PUT document/:id)构建的,但@document是根据其“true”类(Document的子类)创建的

如果你有撬(强烈推荐),请写:

def update
  @document = Document.find(params[:id])
  binding.pry
  # ...
end

然后检查@document。即使您在表单中调用了@document.becomes(Document),您也会看到@document是LegalDocument的实例或其他子类。

因此,最终f.object@document不一样。

这解释了为什么在验证失败时看不到f.object.errors的原因。

修改

处理STI和表单的“最佳方式”是不使用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]仅此而已。就像一个经典的资源。

如果您想在不同的操作(通常为editnew)之间共享此表单,请执行以下操作:

<%= 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' %>

答案 1 :(得分:1)

几乎它是一个错误,它应该按照你最初的预期工作。解决这个问题的以下补丁看起来像是在10月份撤回了

https://github.com/lazyatom/rails/commit/73cb0f98289923c8fa0287bf1cc8857664078d43