You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails中验证错误仅在奇数次提交时显示的问题

问题描述

提交无效时间时出现循环异常:第一次提交会显示错误提示;第二次提交无效时间,错误提示消失且时间字段重置为之前的有效值;第三次提交又会显示错误,第四次重复第二次的问题。

相关代码如下:

编辑视图(edit.html.erb)

<h1>Edit</h1>

<%= render "form", appointment: @appointment, from: @source %>

<div>
  <% if params[:source] == "confirmed" %>
    <%= link_to "Volver atras", confirmed_path %>
  <% elsif params[:source] == "request" %>
    <%= link_to "Volver atras", requests_path %>
  <% elsif params[:source] == "user" %>
    <%= link_to "Volver atras", @appointment %> |
  <% end %>  
</div>

表单部分(_form.html.erb)

<%= form_with(model: appointment) do |form| %>
  <% if appointment.errors.any? %>
    <div style="color: red">
      <h2> Tu turno tiene <%= appointment.errors.count %> error/es:</h2>

      <ul>
        <% appointment.errors.each do |error| %>
          <li><%= error.full_message.split(" ")[1..].join(" ") %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= form.label :time, style: "display: block" %>
    <%= form.datetime_field :time%>
  </div>

  <div>
    <%= form.hidden_field :source, value: @from %>
    <%= form.submit %>
  </div>
<% end %>

Appointment控制器update方法

def update
  respond_to do |format|
    ant= @appointment.state

    if params[:appointment][:source] == "request" 
      @appointment.state= 3

      if @appointment.update(appointment_params)
        format.html { redirect_to requests_path, notice: "Turno actualizado, espere la confirmación." }
        format.json { render :show, status: :ok, location: @appointment }
      else
        @appointment.state= ant 
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @appointment.errors, status: :unprocessable_entity }
      end
    end
  end
end
问题原因
  1. 对象状态混乱:手动修改@appointment.state后又在验证失败时回滚,干扰了Rails对对象属性的跟踪,导致验证失败时time属性无法保留用户提交的无效值,重置为之前的有效值。
  2. 逻辑覆盖不全:update方法仅处理source == "request"的场景,其他source无处理逻辑,可能触发意外状态重置。
  3. 参数处理不当:直接修改对象属性后调用update,导致对象内存状态与提交参数不一致,渲染视图时使用对象旧状态填充表单。
解决方案

1. 修正控制器update方法,整合状态修改到参数中

取消手动修改对象属性的操作,将state修改合并到参数中统一处理:

def update
  respond_to do |format|
    # 准备要更新的参数,根据source追加state设置
    update_params = appointment_params.to_h
    update_params[:state] = 3 if params[:appointment][:source] == "request"

    if @appointment.update(update_params)
      # 根据source跳转对应页面
      redirect_path = case params[:appointment][:source]
                      when "request" then requests_path
                      when "confirmed" then confirmed_path
                      when "user" then @appointment
                      end
      format.html { redirect_to redirect_path, notice: "Turno actualizado." }
      format.json { render :show, status: :ok, location: @appointment }
    else
      # 验证失败时保留source参数,渲染编辑页
      @source = params[:appointment][:source]
      format.html { render :edit, status: :unprocessable_entity }
      format.json { render json: @appointment.errors, status: :unprocessable_entity }
    end
  end
end

private
# 确保params允许state字段
def appointment_params
  params.require(:appointment).permit(:time, :source, :state)
end

2. 完善edit动作的@source赋值

在控制器edit动作中设置@source,确保表单hidden字段能正确获取值:

def edit
  @source = params[:source]
end

3. 验证表单参数保留逻辑

修改后,验证失败时@appointment会保留用户提交的无效time值,表单将自动显示用户输入内容并展示错误提示,不会出现字段重置的情况。

效果验证
  • 每次提交无效时间都会稳定显示错误提示,表单保留用户输入的无效值。
  • 不同source场景的跳转逻辑正常执行。

内容的提问来源于stack exchange,提问作者Santiago Cueva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 16:33:16