Rails Gym模型自定义验证方法sign_only_one_waiver未触发问题排查
问题根源
- 你将校验逻辑写在了
Gym模型中,但你触发创建操作的是UserGymWaiver实例,两者的验证生命周期完全独立,创建UserGymWaiver不会触发Gym的任何验证,因此你写的sign_only_one_waiver方法永远不会被调用。 - 现有
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验证的极端情况,保证数据层面的唯一性:
- 生成迁移文件:
rails g migration AddUniqueIndexToUserGymWaivers user_id:integer gym_id:integer - 修改迁移文件内容:
class AddUniqueIndexToUserGymWaivers < ActiveRecord::Migration[7.0] def change add_index :user_gym_waivers, [:user_id, :gym_id], unique: true end end
- 执行迁移:
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
相关产品推荐
相关产品推荐

