Rails, STI и 'gets' - не отображаются f.object.errors - PullRequest
2 голосов
/ 08 декабря 2011

Мой вопрос: почему .becomes не передает ошибки новому объекту? Разве это не ожидаемое поведение?


У меня есть следующие классы наследования одной таблицы в приложении rails:

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

class LegalDocument < Document
end

class MarketingDocument < Document
end

Я хочу использовать один и тот же контроллер и набор представлений для редактирования LegalDocument с и MarketingDocument с, поэтому я использую 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 %>
  • Если заголовок заполнен, документы обновляются корректно.
  • Если заголовок оставить пустым, я возвращаюсь в режим редактирования, НО ошибка не отображается.

Из отладки я знаю, что он не отображается, потому что f.object.errors - ноль. Однако после отладки я также знаю, что @document.errors НЕ равно нулю, как и ожидалось.


Мой вопрос: почему .becomes не передает ошибки новому объекту? Разве это не ожидаемое поведение?

Ответы [ 2 ]

2 голосов
/ 08 декабря 2011

Да, я тоже это заметил.

Просто измените 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 создается в соответствии со своим «истинным» классом (подклассом Document). *

Если у вас есть pry (настоятельно рекомендуется), напишите:

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

А затем осмотрите @document. Вы увидите, что @document является экземпляром LegalDocument или другого подкласса, даже если вы назвали @document.becomes(Document) в своей форме.

Так что в финале f.object и @document не совпадают.

Это объясняет, почему вы не видите f.object.errors, когда проверка не пройдена.

Редактировать

«Лучший способ» справиться с ИППП и формой - НЕ использовать 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' %>
1 голос
/ 08 декабря 2011

В значительной степени это ошибка, и она должна работать так, как вы изначально ожидали. Следующий патч для решения проблемы выглядит так, как будто он был выпущен в октябре

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

...