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

Rails Gym模型自定义验证方法sign_only_one_waiver未触发问题排查

问题根源

  1. 你将校验逻辑写在了Gym模型中,但你触发创建操作的是UserGymWaiver实例,两者的验证生命周期完全独立,创建UserGymWaiver不会触发Gym的任何验证,因此你写的sign_only_one_waiver方法永远不会被调用。
  2. 现有sign_only_one_waiver逻辑本身存在错误:
    • 你要实现的是「同一用户在同一健身房仅能签署一份免责声明」,但现有查询没有过滤user_id,判断逻辑是某健身房总签署记录大于1就报错,完全不符合需求
    • Gym模型实例的主键是id,你调用的self.gym_id在Gym类中是不存在的属性,值永远为nil,查询条件本身就不生效

修复方案

步骤1:将验证逻辑移到UserGymWaiver模型

修改app/models/user_gym_waiver.rb,在创建UserGymWaiver实例时自动触发校验:

class UserGymWaiver < ApplicationRecord
  # 保留你原有已定义的关联关系
  belongs_to :user
  belongs_to :gym_waiver

  # 新增自定义验证,创建时触发
  validate :check_unique_waiver_sign, on: :create

  private

  def check_unique_waiver_sign
    sign_existed = UserGymWaiver.exists?(user_id: user_id, gym_id: gym_id)
    errors.add(:base, "同一家健身房仅能签署一次免责声明") if sign_existed
  end
end

步骤2(可选):添加数据库唯一索引

避免并发请求绕过Rails验证的极端情况,保证数据层面的唯一性:

  1. 生成迁移文件:
    rails g migration AddUniqueIndexToUserGymWaivers user_id:integer gym_id:integer
  2. 修改迁移文件内容:
class AddUniqueIndexToUserGymWaivers < ActiveRecord::Migration[7.0]
  def change
    add_index :user_gym_waivers, [:user_id, :gym_id], unique: true
  end
end
  1. 执行迁移:
    rails db:migrate

步骤3(可选):优化控制器错误返回

你当前使用create!方法,验证失败时会直接抛出异常,可调整为友好错误返回,修改app/controllers/api/v1/gyms_controller.rb的accept_waiver方法:

def accept_waiver
  gym_waiver = GymWaiver.find_by(gym_id: @gym.id)
  waiver_signed_by_user = UserGymWaiver.new(user_id: current_user.id, gym_id: gym_waiver.gym_id)

  if waiver_signed_by_user.save
    render json: UserGymWaiverBlueprint.render(waiver_signed_by_user, root: :data)
  else
    render json: { errors: waiver_signed_by_user.errors.full_messages }, status: :unprocessable_entity
  end
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 12:27:05