2009-10-08 12 views
7

Estoy probando el foro beast escrito en rieles y lo usaré como un ejemplo de un problema que sigo enfrentando.validación de los rieles sobre el redireccionamiento

El foro tiene una acción de temas/presentación y se ve con un formulario en la parte inferior para crear una nueva publicación dentro del tema.

Enviando el formulario a posts/create y si la validación pasa redirige a temas/show y funciona bien, sin embargo si la validación falla (dejando fuera el campo del cuerpo) se le redirige a los mismos temas/show y volver al formulario, sin errores de validación ... Normalmente, si la validación falla, se queda en lo que sea/create con render: action => new.

¿Se están perdiendo las validaciones en la redirección y cuál es el mejor método para hacerlo funcionar?

Ver código de abajo:

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 

temas/mostrar vista

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

Muchas gracias.

+0

El flujo de trabajo parece raro – Chirantan

+0

¿Qué quieres decir? Probablemente podría haberlo preguntado más simplemente sin el ejemplo. Básicamente, ¿qué sucede con los mensajes de error si los redirige en lugar de procesarlos, y dónde se puede acceder? – sebastyuiop

Respuesta

12

Creo que tiene dos problemas aquí.

  1. Mantener los errores de validación a través de una redirección
  2. Volver a llenar los campos del formulario para que el usuario no tiene que introducir toda la información de nuevo.

Ambas cosas están conectadas.

Los errores de validación generalmente se muestran a través del método error_msg_for que actúa sobre un objeto. Por lo general, el controlador lo proporciona como una variable de instancia de un objeto que no se pudo guardar. Esa misma variable de instancia se usa para volver a llenar el formulario.

Durante una redirección, el controlador generalmente creará una instancia de una variable de instancia utilizando hash de params. Por lo tanto, cualquier información utilizada para determinar por qué se perdió un error de guardado. Los recursos normales se mostrarán en caso de error de guardado y se redirigirá en caso de éxito, esto provoca que ocurran dos cosas.

  1. La instancia del objeto se pasa a error_msg_para crear ese bonito cuadro de error uniforme.
  2. La instancia del objeto se utiliza para rellenar los campos del formulario, lo que le permite a su usuario editar solo lo que sea necesario.

No conozco Bestia tan bien, así que no estoy seguro si la forma de crear hilos es un modelo de registro activo. Pero lo siguiente le dará una idea de cómo solucionar su problema. Implicaría modificar tu copia local del complemento Beast, por lo que si estás utilizando una herramienta para mantenerla actualizada, es posible que pierdas los cambios.

He estado utilizando estos métodos para obtener sus problemas de validación. Suponiendo que el formulario del que se trata está basado en un nmodel, deberían proporcionarle todo lo que necesita para volver a llenar un formulario.

Básicamente, almacena una copia superficial del objeto con los errores en el hash flash, utilizando clone_with_errors. Debe usar una copia superficial o de lo contrario se encontrará con problemas cuando muestre errores para registros con múltiples asociaciones.

Luego uso my_error_msg_for que toma las mismas opciones que el error_msg_for estándar para generar los mensajes de error html. Solo lo escribí porque, por alguna razón, el método estándar error_msg_for no funcionaba con los objetos almacenados en el hash. Es casi idéntico a la versión fuente oficial de error_msg_for que fue problemático.

/app/controllers/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 

/app/views/examples/show.html.erb

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

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

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

Aquí está el código que necesita para que sea todo el trabajo.

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 
+0

solo una nota, el método add_to_base se eliminó de los rieles 3. En su lugar, debe usar los errores [: base] << "error". – damoiser

4

Me temo que no sé nada de Bestia, pero hablando genéricamente, todo se pierde cuando se redirige. Es una nueva solicitud de página, y todo se restablece a menos que se haya almacenado en alguna parte (la base de datos o la sesión, normalmente).

El flujo normal que se suele ver con formularios es redirigir si el objeto se guarda, pero para representar si el guardado falla El archivo de vista puede recoger las variables que se hayan establecido en el controlador, que normalmente incluirían el objeto que no guardó y sus mensajes de validación.

Disculpe, eso no resuelve su problema, pero con suerte le puede dar algunas pistas.

+0

Gracias por la explicación. Obtengo cómo funciona normalmente, pero cuando tienes un formulario para publicaciones en una página de tema de programa (lo cual es bastante normal) no puedes renderizar: acción => nuevo en el controlador de mensajes ya que no tendrás acceso a los objetos disponible en la acción del tema del programa a menos que lo duplique y agregue una vista adicional para /posts/new.erb.html. Sería bueno si pudieras renderizar: action => "topics/show" en el controlador de mensajes. Gracias por tu ayuda. – sebastyuiop

2

My answer a a very similar question publicado más recientemente aquí en StackOverflow abarca una serie de pros y contras a la redirect_torender vs. debate. Me encantaría saber si alguien tiene otros pros/contras para agregar a la discusión.

+0

He intentado renderizar. Funciona, solución simple y estúpida. – workdreamer

Cuestiones relacionadas