【发布时间】:2018-03-29 03:56:41
【问题描述】:
我有一个模型 Prediction,它的实例通过属性 :participant_id :match_id 引用了其他模型 Participant 和 Match。
这个想法是@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: 2 和 participant_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