【问题标题】:Rails. Debugging a Uniqueness validation with scope that fails when object is updated导轨。使用对象更新时失败的范围调试唯一性验证
【发布时间】:2018-03-29 03:56:41
【问题描述】:

我有一个模型 Prediction,它的实例通过属性 :participant_id :match_id 引用了其他模型 ParticipantMatch

这个想法是@participant 每个@match 只能有一个@prediction(每个@participant 每个@match 最多只能给出一个预测)。

所以我在我的预测模型中加入了以下验证

validates :match_id, uniqueness: { scope: [:participant_id] }

当同一个@match 和@participant 已经有一个@prediciton 时,验证可以禁止创建@prediction 对象。问题在于,即使我没有更改原始对象的分配 ID,它也会阻止更新对象。另外,它让我只更新一个 Prediction 实例,所有其他实例都发生错误。

令人费解的是,它工作正常,但由于某种原因,我无法跟踪,它停止工作,而且我已经没有关于如何调试的想法。你能提供任何想法吗?在我检查/尝试过的内容下方:

失败请求的相关参数:

"prediction"=><ActionController::Parameters {"participant_id"=>"1", "match_id"=>"2", "local_score"=>"2", "visitant_score"=>"0"}

我得到的错误:

 @details={:match_id=>[{:error=>:taken, :value=>2}]},
 @messages={:match_id=>["has already been taken"]}>

match_id: 2 的数据库中有很多预测。但是,尽管数据库中没有其他 @prediction 与 match_id: 2participant_id: 1 一起出现,但这种情况仍然发生,当然,除了在更新时引发此错误的实例。

有趣的是,如果我在控制台中尝试相同的操作(如下所示),它会成功更新。

@prediction = Prediction.find_by(participant_id: 1, match_id: 2)
@prediction.update_attributes(local_score: 8, visitant_score: 8)

暗示问题出在控制器动作中:

app/controllers/predicitons_controller.rb

  def update
    respond_to do |format|
      if @prediction.update(prediction_params)
        format.json { render :show, status: :ok, location: @prediction }
        format.js
      else
        @errorMessages = []
        @prediction.errors.full_messages.each do |message|
            @errorMessages << message
        end
        format.js {render template: 'predictions/update_error'}
      end
    end
  end

我认为那里没有问题。此外,似乎发送给控制器的唯一成功更新的请求是:

 "prediction"=><ActionController::Parameters {"participant_id"=>"1", "match_id"=>"1", "local_score"=>"1", "visitant_score"=>"1"}

如果我做其他这些,例如,它不会:

"prediction"=><ActionController::Parameters {"participant_id"=>"2", "match_id"=>"1", "local_score"=>"0", "visitant_score"=>"0"}

"prediction"=><ActionController::Parameters {"participant_id"=>"2", "match_id"=>"2", "local_score"=>"9", "visitant_score"=>"9"}

"prediction"=><ActionController::Parameters {"participant_id"=>"4", "match_id"=>"1", "local_score"=>"1", "visitant_score"=>"1"

所有这些失败的请求都会抛出“match_id has been taken”错误,并且在控制台中更新得很好。

控制器请求正在使用方法::put 触及正确的操作。我试图更改验证切换 match_id 为 pool_id 的语法(这当然不应该有所作为)但没有成功。

任何帮助将不胜感激。


app/models/prediction.rb

class MatchPrediction < ApplicationRecord
  belongs_to :participant
  belongs_to :match
  validates :match_id, uniqueness: { scope: [:pool_participant_id] }
  def correct_score?
    if match.official_outcome
      local_score == match.local_score && visitant_score == match.visitant_score ? true : false
    end
  end
  def correct_outcome?
    if match.official_outcome
      predicted_outcome == match.official_outcome ? true : false
    end
  end
end

和服务器输出预回滚:

Started PUT "/pools/1/predictions/19" for 127.0.0.1 at 2018-03-29 18:17:11 +1100
Processing by PredictionsController#update as JS
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"SagJX+a7m0eCAdH7AdA0eYz6BVL1cesXYXOUkoe2FRynta6wyiWdskrC1007V1vyrIApPtdEQnVHWlzhSeJs5Q==", "prediction"=>{"participant_id"=>"4", "match_id"=>"2", "local_score"=>"7", "visitant_score"=>"0"}, "pool_id"=>"1", "id"=>"19"}
  User Load (1.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 4], ["LIMIT", 1]]
  ↳ /Users/andres/.rvm/gems/ruby-2.5.0@global/gems/rack-2.0.4/lib/rack/tempfile_reaper.rb:15
  Prediction Load (0.4ms)  SELECT  "predictions".* FROM "predictions" WHERE "predictions"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/predictions_controller.rb:86
   (0.2ms)  BEGIN
  ↳ app/controllers/predictions_controller.rb:55
  Participant Load (0.2ms)  SELECT  "participants".* FROM "participants" WHERE "participants"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
  ↳ app/controllers/predictions_controller.rb:55
  Match Load (0.3ms)  SELECT  "matches".* FROM "matches" WHERE "matches"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
  ↳ app/controllers/predictions_controller.rb:55
  Prediction Exists (1.8ms)  SELECT  1 AS one FROM "predictions" WHERE "predictions"."match_id" = $1 AND "predictions"."id" != $2 AND "predictions"."participant_id" = $3 LIMIT $4  [["match_id", 2], ["id", 1], ["participant_id", 4], ["LIMIT", 1]]
  ↳ app/controllers/predictions_controller.rb:55
  Pool Load (0.5ms)  SELECT  "pools".* FROM "pools" WHERE "pools"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/models/prediction.rb:35
  Round Load (0.6ms)  SELECT  "rounds".* FROM "rounds" INNER JOIN "groups" ON "rounds"."id" = "groups"."round_id" WHERE "groups"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/models/prediction.rb:21
   (0.2ms)  ROLLBACK
  ↳ app/controllers/predictions_controller.rb:55

【问题讨论】:

  • 让我们看看预测模型,并看看你的服务器控制台输出的查询,它正在检查唯一性验证。
  • 在您的查找中,您通过 match_id 和参与者 ID 进行搜索。在您的控制器中,您如何找到记录?按 ID?
  • 大家感谢您的帮助。 @GorillaApe 我想我在您的指导下发现了错误。我错过了重要的部分,忘记检查我是如何在控制器中设置@prediction 的。我不敢相信我错过了这个。由于我的路线最近发生了变化,我的控制器总是设置相同的实例(我可以更新而不会失败的实例)。我会修改它,我有信心能够解决它。
  • 好的,如果可以的话,添加更多信息。

标签: ruby-on-rails validation activerecord controller


【解决方案1】:

真是令人尴尬的错误。我错过了检查预测控制器如何设置@prediction。正如@GorillaApe 建议的那样,我输入了参数以错误地找到对象:

def set_match_prediction
  @prediction = Prediction.find(params[:pool_id])
end 

代替:

def set_match_prediction
  @prediction = Prediction.find(params[:id])
end

这反过来总是设置相同的对象,因此在除一个实例之外的所有实例中都会引发验证错误,其中一个实例是偶然的,它的 :id 与 :pool_id 重合。

【讨论】:

    猜你喜欢
    • 2020-03-30
    • 2013-05-01
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多