【问题标题】:Keep changes on reload if validation fails如果验证失败,请在重新加载时保留更改
【发布时间】:2016-05-21 14:50:06
【问题描述】:

我正在使用 Rails 中的验证,例如:

validates_presence_of :some_field

我注意到,如果验证失败,所有更改都会被数据库中的现有值覆盖。这是有道理的,因为页面基本上正在重新加载(正如我从我的开发日志中收集的那样),但是这增加了用户错误/沮丧的风险,因为一个字段中的单个错误将需要倒霉的家伙重新输入他对所有领域所做的更改。

我的问题:如果验证失败,如何让 rails 重新加载刚刚提交的数据?这样,用户可以更正错误,而无需重新输入其余的修订。

感谢您的建议。

编辑: 根据要求,我的更新方法如下:

def update
    @incorporation = Incorporation.find(params[:id])
    @company = @incorporation.company
    begin
        @company.name="#{params[:company][:names_attributes].values.first["name_string"]} #{params[:company][:names_attributes].values.first["suffix"]}"
    rescue NoMethodError
        @company.name="Company #{@company.id} (Untitled)"
    end
    if @company.update(company_params)
        redirect_to incorporations_index_path
    else
        redirect_to edit_incorporation_path(@incorporation)
    end
end

关于我的控制器的完整披露:上面的update 来自我的incorporations_controller,即使我正在更新我的Company 模型。 Company has_one :incorporation。我这样做是因为,在我的应用程序的更大范围内,它使我的关联更加清晰。

【问题讨论】:

  • 你能告诉我们你的代码吗def update
  • @Lymuel 感谢您的回复。我已经用我的更新方法更新了我的问题。根据您的请求,我认为验证应该在理论上在失败时加载包含提交数据的页面?

标签: ruby-on-rails validation ruby-on-rails-4 model rails-activerecord


【解决方案1】:

要添加到正确答案,您可以稍微清理一下代码:

def update
    @incorporation = Incorporation.find params[:id]

    respond_to do |format|
      if @incorporation.update company_params
        format.html { redirect_to({:action => "index"})}
      else
        format.html { render :edit }
        format.json { render json: @incorporation.errors, status: :unprocessable_entity }
      end
    end
end

如果你使用accepts_nested_attributes_for,你绝对应该破解前端的相关对象。

您应该查找fat model, skinny controller(让模型完成工作):

#app/models/company.rb
class Company < ActiveRecord::Base
  before_update :set_name
  attr_accessor :name_string, :name_suffix

  private

  def set_name
    if name_string && name_suffix
      self[:name] = "#{name_string} #{name_suffix}"
    else
      self[:name] = "Company #{id} (Untitled)"
    end
  end
end

这将允许您填充“公司”的name。直接编辑嵌套/关联对象是antipattern;稍后会再次困扰您的黑客攻击。


答案的关键是:render :edit

渲染编辑视图意味着您当前的@company / @incorporation 数据被保留。

重定向将调用controller 的新实例,覆盖@incorporation,因此您会在前端看到。

【讨论】:

  • 您好 Rich,感谢您的回答。我一直认为在模型中填充名称是个好主意。那么我还没有这样做的唯一原因是因为几个控制器引用了这个模型,并且只有一个控制器需要执行这个分配过程。 (原因是在注册过程中,一家公司可能有许多需要使用商标搜索检查的可能名称)但是现在我正在进行验证,可能是时候解决这个问题了。
  • 如果你给我一些上下文,如果你愿意,我会写一些代码。诀窍是始终确保您的代码是可重用的;您的控制器代码不是,而且很hacky。这种 hackiness 最终可能会破坏您的应用程序,而您将完全不知道为什么(然后花费数小时试图修复它)。
  • 感谢您的提议,但我想我已经找到了解决方案。我正在使用attr_accessor 创建一个属性:context,它指定了我正在使用的控制器。基于该值,我在模型中执行必要的特定于控制器的操作。
  • 我打算推荐它!感谢回复
  • 哈!学生变成了主人。 (至少现在):-D ...但是如果您正在寻找一个难题,我目前正在努力解决我刚刚发布的另一个与验证相关的问题。 (当然没有压力)
【解决方案2】:

更新你的控制器到这个

def update
    @incorporation = Incorporation.find(params[:id])
    @company = @incorporation.company
    begin
        @company.name="#{params[:company][:names_attributes].values.first["name_string"]} #{params[:company][:names_attributes].values.first["suffix"]}"
    rescue NoMethodError
        @company.name="Company #{@company.id} (Untitled)"
    end
    respond_to do |format|
        if @company.update(company_params)
            format.html { redirect_to({:action => "index"})}
        else
            format.html{render :edit}
            format.json { render json: @incorporation.errors, status: :unprocessable_entity }
        end
    end
end

【讨论】:

  • 感谢您的回复。以上工作正常,但现在,如果出现异常,它将指向显示路由/incorporations/3,并呈现:edit(我本身对此没有任何真正的问题)。但是当我尝试从中保存时,似乎决定重定向到索引,无论我输入的内容是否有效。我要求它在update 中呈现什么也没关系。知道为什么会发生这种情况吗?
  • 所有我认为您希望在成功保存后将其重定向到您的 index,使用它转到您的 show format.html { redirect_to @incorporation} 而不是format.html { redirect_to({:action =&gt; "index"})}
  • 我确实试过了。问题是它似乎正在重定向到索引,即使保存不成功并且无论我将它指向何处。
  • 没关系,这个问题与我对这个特定表单的路由有关。非常感谢,您的解决方案非常完美。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-24
  • 2011-01-10
  • 1970-01-01
  • 1970-01-01
  • 2011-06-14
相关资源
最近更新 更多