Проверка Rails через перенаправление - PullRequest
8 голосов
/ 08 октября 2009

Я пробую форум beast , написанный на rails, и буду использовать это как пример проблемы, с которой я продолжаю сталкиваться.

На форуме есть тема / показать действие и вид с формой внизу для создания нового сообщения в теме.

Отправка формы идет в posts / create, и если проверка проходит перенаправления обратно в разделы / шоу и работает нормально, однако, если проверка завершается неудачно (без поля body), вы перенаправляетесь на те же темы / show и обратно в форму, без ошибок валидации ... обычно, если валидация не удалась, вы остаетесь на что угодно / create с render: action => new.

Проверки теряются в перенаправлении, и каков наилучший способ заставить его работать?

См. Код ниже:

PostsController.rb

  def create
    @post = current_user.reply @topic, params[:post][:body]

    respond_to do |format|
      if @post.new_record?
        format.html { redirect_to forum_topic_path(@forum, @topic) }
        format.xml  { render :xml  => @post.errors, :status => :unprocessable_entity }
      else
        flash[:notice] = 'Post was successfully created.'
        format.html { redirect_to(forum_topic_post_path(@forum, @topic, @post, :anchor => dom_id(@post))) }
        format.xml  { render :xml  => @post, :status => :created, :location => forum_topic_post_url(@forum, @topic, @post) }
      end
    end
  end

TopicsController.rb

  def show
    respond_to do |format|
      format.html do
        if logged_in?
          current_user.seen!
          (session[:topics] ||= {})[@topic.id] = Time.now.utc
        end
        @topic.hit! unless logged_in? && @topic.user_id == current_user.id
        @posts = @topic.posts.paginate :page => current_page
        @post  = Post.new
      end
      format.xml  { render :xml  => @topic }
    end
  end

Темы / Показать представление

  <% form_for :post, :url => forum_topic_posts_path(@forum, @topic, :page => @topic.last_page) do |f| %>

  <%= f.error_messages %>

  <table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tr>
      <td rowspan="2" width="70%">
        <%= f.text_area :body, :rows => 8 %>
      </td>
      <td valign="top">
        <%= render :partial => "posts/formatting" %>
      </td>
    </tr>
    <tr>
      <td valign="bottom" style="padding-bottom:15px;">
       <%= submit_tag I18n.t('txt.views_topics.save_reply', :default => 'Save reply') %>
     </td>
   </tr>
  </table>
  <% end %>

Большое спасибо.

Ответы [ 3 ]

13 голосов
/ 09 октября 2009

Я думаю, у вас есть две проблемы здесь.

  1. Сохранение ошибок проверки через перенаправление
  2. Повторное заполнение полей формы, чтобы пользователю не приходилось вводить всю информацию снова.

Обе эти вещи связаны.

Ошибки проверки обычно отображаются с помощью метода error_msg_for, который воздействует на объект. Обычно предоставляется контроллером как переменная экземпляра объекта, которая не может быть сохранена. Эта же переменная экземпляра используется для повторного заполнения формы.

Во время перенаправления контроллер обычно создает экземпляр переменной экземпляра, используя хэш params. Поэтому любая информация, используемая для определения причины сбоя сохранения, теряется. Обычные ресурсы будут отображаться при сбое сохранения и перенаправляться при успехе, это приводит к двум вещам.

  1. Экземпляр объекта передается в error_msg_ для создания этого красивого единообразного блока ошибок.
  2. Экземпляр объекта используется для заполнения полей формы, что позволяет вашему пользователю редактировать только то, что необходимо.

Я не очень хорошо знаю Beast, поэтому я не уверен, является ли форма для создания потоков активной моделью записи. Но следующее даст вам представление о том, как обойти вашу проблему. Для этого потребуется изменить локальную копию плагина Beast, поэтому, если вы используете инструмент для его обновления, ваши изменения могут быть потеряны.

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

По сути, вы сохраняете поверхностную копию объекта с ошибками во флеш-хэше, используя clone_with_errors. Вы должны использовать поверхностную копию, иначе вы столкнетесь с проблемами при отображении ошибок для записей с несколькими ассоциациями.

Затем я использую my_error_msg_for, который использует те же опции, что и стандартный error_msg_for, для создания html сообщений об ошибках. Я написал это только потому, что по какой-то причине стандартный метод error_msg_for не работал с объектами, хранящимися в хэше. Это почти идентично официальной исходной версии error_msg_for, которая вызывала беспокойство.

/ приложение / контроллеры / examples_controller.rb

class ExamplesController < ApplicationController
  def update
    ...

    if @example.save 
      regular action
    else
      flash[:errors] = clone_with_errors(@example)
      respond_to do |format|
        format.html redirect_to(@example)
      end
    end
end

/ приложение / просмотров / примеры / show.html.erb

<div id="error">
        <% if flash[:errors] && !flash[:errors].empty? then -%>

        <p ><%= my_error_msg_for flash[:errors] %></p>

        <% end -%>
</div>
...

Вот код, который вам нужен, чтобы все это заработало.

application_controller.rb

 def clone_with_errors(object)
    clone = object.clone
    object.errors.each{|field,msg| clone.errors.add_to_base(msg)}
    return clone
  end

application_helper.rb

 def _error_msg(*params)

    options = params.extract_options!.symbolize_keys
    if object = options.delete(:object)
      objects = [object].flatten
    else
      objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
    end
    count   = objects.inject(0) {|sum, this| sum + this.errors.count }
    unless count.zero?
      html = {}
      [:id, :class].each do |key|
        if options.include?(key)
          value = options[key]
          html[key] = value unless value.blank?
        else
          html[key] = 'errorExplanation'
        end
      end
      options[:object_name] ||= params.first
      options[:header_message] = "#{pluralize(count, 'error')} prohibited this #{options[:object_name].to_s.gsub('_', ' ')} from being saved" unless options.include?(:header_message) && !options[:header_messag].nil?
      options[:message] ||= 'There were problems with the following fields:' unless options.include?(:message) && !options[:message].nil?
      error_messages = objects.sum {|this| this.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join

      contents = ''
      contents << content_tag(options[:header_tag] || :h2, options[:header_message]) unless options[:header_message].blank?
      contents << content_tag(:p, options[:message]) unless options[:message].blank?
      contents << content_tag(:ul, error_messages)

      content_tag(:div, contents, html)
    else
                                        ''
    end

  end

  def my_error_msg_for(params)
    _error_msg_test :object_name => params[:object].class.name.gsub(/([a-z])([A-Z])/,'\1 \2').gsub(/_/, " "),
    :object => params[:object], :header_message => params[:header_message], :message => params[:message]
  end
5 голосов
/ 08 октября 2009

Боюсь, я ничего не знаю о Beast, но, говоря в общем, все теряется, когда вы перенаправляете. Это новый запрос страницы, и все сбрасывается, если только оно не было сохранено где-либо (обычно это база данных или сеанс).

Обычный поток, который вы склонны видеть в формах, - это перенаправление, если объект сохранен, и рендеринг, если сохранение не удалось. Файл представления может затем выбрать любые переменные, которые были установлены в контроллере - которые обычно включают объект, который не был сохранен, и его сообщения проверки.

Извините, это не решает вашу проблему, но, надеюсь, это даст вам некоторые подсказки.

2 голосов
/ 03 января 2012

Мой ответ на очень похожий вопрос , опубликованный совсем недавно здесь в StackOverflow, охватывает ряд плюсов и минусов в дискуссии redirect_to против render. Я хотел бы услышать, есть ли у кого-то еще плюсы / минусы, которые можно добавить к обсуждению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...