2

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


我在 Rails 应用程序中有以下单表继承类:

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

class LegalDocument < Document
end

class MarketingDocument < Document
end

我想使用相同的控制器和一组视图来编辑LegalDocuments 和MarketingDocuments,所以我使用DocumentsController < ApplicationController以下editupdate操作:

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是 nil。 但是,从调试中,我也知道@document.errors不是 nil,正如预期的那样。


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

4

2 回答 2

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) (=> PUT document/:id) 构建的,但 @document 是根据其“true”类(Document 的子类)创建的。

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

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

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

所以在finalf.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' %>
于 2011-12-08T16:57:49.190 回答
1

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

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

于 2011-12-08T18:01:52.463 回答