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

如何在Rails的before_save回调中验证用户与健身房Geocoder对象的distance_to小于200米以创建CheckIn记录?

Fixing Your Check-In Distance Validation in Rails

First off, great start with using Geocoder for this check-in distance validation! Let's work through the issues in your code to get it working properly:

Key Problems in Your Current Code

  • Models can't access params: Your CheckIn model is trying to use params[:gym_id], but params is a controller-only object—models don't have access to it.
  • Hardcoded coordinates: You're using [43.9,-98.6] instead of the user's actual check-in location that's being submitted.
  • Inefficient Gym lookup: Since CheckIn belongs_to :gym, you don't need to query the gym again—you can just use the already-associated self.gym.
  • Validation doesn't block invalid saves: Your before_save method only returns true when the distance is valid, but doesn't prevent saves when it's invalid (and doesn't add error messages for debugging).
  • Controller doesn't handle failures: Using create! will raise an exception if validation fails, which will crash your API instead of returning a useful error response.

Corrected Code

app/models/check_in.rb

class CheckIn < ApplicationRecord
  belongs_to :user
  belongs_to :gym
  before_save :validate_check_in_distance

  protected
  def validate_check_in_distance
    # Use the associated gym instead of re-querying
    gym_location = [gym.latitude, gym.longitude]
    # Use the check-in's submitted coordinates instead of hardcoding
    user_check_in_location = [check_in_latitude, check_in_longitude]
    
    # Calculate distance (specify meters explicitly to avoid unit confusion)
    distance_to_gym = Geocoder::Calculations.distance_between(gym_location, user_check_in_location, units: :meters)

    if distance_to_gym >= 200
      # Add an error message and return false to block the save
      errors.add(:base, "You must be within 200 meters of the gym to check in")
      return false
    end
    true
  end
end

app/controllers/api/v1/check_ins_controller.rb

class Api::V1::CheckInsController < ApplicationController
  before_action :authenticate_request!

  def check_in
    @check_in = CheckIn.new(check_in_params.merge(user: current_user))
    
    if @check_in.save
      render json: CheckInBlueprint.render(@check_in, root: :data), status: :created
    else
      # Return meaningful error messages if validation fails
      render json: { errors: @check_in.errors.full_messages }, status: :unprocessable_entity
    end
  end

  private
  def check_in_params
    params.require(:check_in).permit(:gym_id, :check_in_latitude, :check_in_longitude)
  end
end

Notes on the Fixes

  • Accessing attributes correctly: The model uses the instance's own check_in_latitude/check_in_longitude and the associated gym's location—this works because the controller passes these params to the model when building the record.
  • Explicit distance calculation: Using Geocoder::Calculations.distance_between makes the logic clearer, and specifying units: :meters ensures you're measuring in the exact unit you need.
  • Error handling: Adding errors to the model lets you return user-friendly feedback, and the controller now gracefully handles both success and failure cases without crashing.
  • Avoiding exceptions: Using new + save instead of create! prevents unhandled exceptions when validation fails, which is critical for a stable API.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:28:11