2009-10-08 57 views
7

我正在尝试使用rails编写的beast论坛,并将其用作我一直面临的问题的一个示例。Rails通过重定向进行验证

论坛有一个主题/显示操作和视图,底部有一个表单以在主题内创建一个新帖子。

提交表单进入文章/创建,如果验证通过重定向回主题/表演,并且工作正常,但是如果验证失败(忽略正文字段),您将被重定向到相同的主题/回到表单,没有验证错误...通常如果验证失败,你将留在任何/使用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 %> 

非常感谢。

+0

工作流本身看起来很奇怪 – Chirantan 2009-10-08 09:06:16

+0

你是什么意思?没有这个例子,我可能会更简单地问这个问题。基本上,如果您重定向而不是渲染,错误消息会发生什么,以及它们可以在哪里访问? – sebastyuiop 2009-10-08 09:14:06

回答

12

我想你在这里有两个问题。

  1. 通过重定向
  2. 保持验证错误,重新填充表单域,使用户不必重新输入所有信息。

这两件事都是连接的。

验证错误通常通过作用于对象的error_msg_for方法显示。通常由控制器提供,作为无法保存的对象的实例变量。该实例变量用于重新填充表单。

在重定向过程中,控制器通常会使用参数散列实例化一个实例变量。因此,用于确定保存失败原因的任何信息都会丢失。正常资源将在保存失败时呈现,并在成功时重定向,这会导致两件事情发生。

  1. 该对象的实例传递给error_msg_for以创建一个漂亮的统一错误框。
  2. 该对象的实例用于填充表单的字段,允许用户只编辑必要的内容。

我不知道野兽如此好,所以我不确定创建线程的表单是否是活动记录模型。但是下面的内容会让你知道如何解决你的问题。这将涉及修改Beast插件的本地副本,因此如果您使用工具来更新它,那么您的更改可能会丢失。

我一直在使用下面的这些方法来解决你的验证问题。假设你所说的表单是基于nmodel的,他们应该为你提供重新填充表单所需的一切。

本质上,您使用clone_with_errors存储闪存散列中存在错误的对象的浅表副本。您必须使用浅拷贝,否则在显示具有多个关联的记录的错误时会遇到问题。

然后,我使用my_error_msg_for,它采用与标准error_msg_for相同的选项来构建错误消息html。我只写了它,因为由于某些原因,标准error_msg_for方法不适用于散列中存储的对象。它与error_msg_for的官方源代码版本几乎完全相同,令人不安。

/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> 
... 

这里有你需要它的代码所有的工作。

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

只是一个说明,add_to_base方法已从rails 3中删除。您应该使用errors [:base] <<“error”代替。 – damoiser 2013-09-11 16:35:11

4

恐怕我对野兽一无所知,但一般而言,当您重定向时,一切都会丢失。这是一个新的页面请求,一切都会被重置,除非它已经存储在某个地方(通常是数据库或会话)。

您倾向于使用窗体看到的正常流程是在保存对象但重新呈现如果保存失败。然后,视图文件可以选择控制器中设置的任何变量 - 通常包括未保存的对象及其验证消息。

对不起,并没有解决你的问题,但希望它可以给你一些线索。

+0

感谢您的解释。我知道它通常是如何工作的,但是当你在展示主题页面上有帖子的表单时(这很正常),你不能在帖子控制器上渲染:action => new,因为你无法访问这些对象除非您复制它并为/posts/new.erb.html添加额外的视图,否则您可以使用展示主题操作。如果您可以在posts控制器中渲染:action =>“topics/show”,那将会很好。谢谢你的帮助。 – sebastyuiop 2009-10-08 09:47:23

2

My answera very similar question发布最近这里在计算器上涵盖了许多利弊的redirect_torender辩论。我很想听听有没有人有任何其他利弊加入讨论。

+0

我试过渲染。它工作,简单而愚蠢的解决方案。 – workdreamer 2015-07-25 22:39:51

相关问题