Rails Postgresql 表中的条目未正确呈现

问题描述

我有一个网络应用程序,可以让用户对电影进行评分。每当用户搜索电影时,都会从外部 api 获取多达 20 部电影的数据并将其发送到我的 Rails 后端,在那里它检查该电影是否已存在于数据库中。如果存在,则返回电影信息。如果它还没有在数据库中,它会在返回信息之前先添加它。

这在一段时间内工作正常,但由于某种原因我不明白,最终数据库中电影中的电影开始表现得很奇怪,它们一一没有显示在 webapp 上,并在我的Rails 日志。如果我在控制器中将 find_or_create_by! 更改为 find_or_create_by(无空格),我不会在日志中收到错误消息,并且大部分电影都正确显示,但它的 :id (不是 api_id),因此不会返回评分和用户关联。

我认为尽管我进行了验证,但可能会创建重复的条目,但是当我在 Rails 控制台中使用 Movie.where(api_id: 269) 检查受影响的电影时,我只得到一个条目,因此似乎并非如此。

我无法破译日志以找出为什么会发生这种情况,一一与数据库中的电影一一对应。我发现让事情恢复正常工作的唯一方法是销毁电影表中的所有电影,但这也意味着我也必须销毁所有评级。我想弄清楚到底是什么原因使条目变得混乱,使它们无法使用。

我的控制器:

class Api::V1::MoviesController < ApplicationController

  def index
      movies =  Movie.all
      render json: movies
  end

  def show
      movie = Movie.find(params[:id])
      render json: movie
  end

  def create
      movie = Movie.find_or_create_by!(movie_params)
      render json: movie
  end

private
  def movie_params
      params.require(:movie).permit(:title,:poster_path,:overview,:api_id,:release_date)
  end

end

我的班级:

class Movie < ApplicationRecord
  has_many :ratings
  has_many :users,through: :ratings
  validates :api_id,uniqueness: true
end

序列化器:

class MovieSerializer < ActiveModel::Serializer
  attributes :id,:title,:release_date
  has_many :ratings
  has_many :users,through: :ratings
end

架构:

  create_table "movies",force: :cascade do |t|
    t.string "title"
    t.string "poster_path"
    t.text "overview"
    t.integer "api_id"
    t.string "release_date"
    t.index ["api_id"],name: "index_movies_on_api_id",unique: true
  end

电影未成功渲染的日志:

Started POST "/api/v1/movies" 
Processing by Api::V1::MoviesController#create as JSON
Parameters: {"api_id"=>269,"title"=>"Breathless","poster_path"=>"/9Wx0Wdn2EOqeCZU4SP6tlS3LOml.jpg","overview"=>"A young car thief kills a policeman and tries to persuade a girl to hide in Italy with him.","release_date"=>"1960-03-16","movie"=>{"title"=>"Breathless","api_id"=>269,"release_date"=>"1960-03-16"}}
User Load (1.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id",1],["LIMIT",1]]
Movie Load (2.3ms)  SELECT "movies".* FROM "movies" WHERE "movies"."title" = $1 AND "movies"."poster_path" = $2 AND "movies"."overview" = $3 AND "movies"."api_id" = $4 AND "movies"."release_date" = $5 LIMIT $6  [["title","Breathless"],["poster_path","/9Wx0Wdn2EOqeCZU4SP6tlS3LOml.jpg"],["overview","A young car thief kills a policeman and tries to persuade a girl to hide in Italy with him."],["api_id",269],["release_date","1960-03-16"],1]]
(1.9ms)  BEGIN
Movie Exists? (1.5ms)  SELECT 1 AS one FROM "movies" WHERE "movies"."api_id" = $1 LIMIT $2  [["api_id",1]]
(2.7ms)  ROLLBACK
Completed 422 Unprocessable Entity in 65ms (ActiveRecord: 9.6ms | Allocations: 10309)
FATAL -- : [8dce0090-dd46-4fc6-8370-08902fa5e13c]   
app/controllers/api/v1/movies_controller.rb:14:in `create'

正确渲染电影的日志:

Started POST "/api/v1/movies" for 67.86.25.53 at 2021-05-04 21:02:22 +0000
Processing by Api::V1::MoviesController#create as JSON
Parameters: {"api_id"=>26317,"title"=>"Man with a Movie Camera","poster_path"=>"/Ded5Kl7MDZQTbFpoNI62tLVWUN.jpg","overview"=>"A cameraman wanders around Moscow,Kharkov,Kiev and Odessa with a camera slung over his shoulder,documenting urban life with dazzling invention.","release_date"=>"1929-01-08","movie"=>{"title"=>"Man with a Movie Camera","api_id"=>26317,"release_date"=>"1929-01-08"}}
User Load (1.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id",1]]
Movie Load (1.3ms)  SELECT "movies".* FROM "movies" WHERE "movies"."title" = $1 AND "movies"."poster_path" = $2 AND "movies"."overview" = $3 AND "movies"."api_id" = $4 AND "movies"."release_date" = $5 LIMIT $6  [["title","Man with a Movie Camera"],"/Ded5Kl7MDZQTbFpoNI62tLVWUN.jpg"],"A cameraman wanders around Moscow,documenting urban life with dazzling invention."],26317],"1929-01-08"],1]]
CACHE User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id",1]]
[active_model_serializers]   rating Load (1.4ms)  SELECT "ratings".* FROM "ratings" WHERE "ratings"."movie_id" = $1  [["movie_id",3745]]
[active_model_serializers]   User Load (1.3ms)  SELECT "users".* FROM "users" INNER JOIN "ratings" ON "users"."id" = "ratings"."user_id" WHERE "ratings"."movie_id" = $1  [["movie_id",3745]]
[active_model_serializers] Rendered MovieSerializer with ActiveModelSerializers::Adapter::Attributes (6.75ms)
Completed 200 OK in 16ms (Views: 5.6ms | ActiveRecord: 5.4ms | Allocations: 2946)

解决方法

主要问题是您将电影的每个属性都传递给 find_by_or_create_by!。这意味着如果任何属性发生更改,Rails 将尝试创建重复记录。这将导致 api_id 上的唯一性约束出现问题。

相反,您想要的是:

Movie.create_with(
  movie_params
).find_or_create_by!(api_id: params.dig(:movie,:api_id))

通过唯一属性查找记录的位置。

或者如果你真的想要更像插入/更新的东西:

Movie.find_or_initialize_by(
  api_id: params.dig(:movie,:api_id)
).update!(movie_params)

如果记录存在来自 API 的新数据,这将更新记录。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...