Ruby on rails Rails 5.2中的ActiveRecord:save/update不保存记录,即使它说保存了

Ruby on rails Rails 5.2中的ActiveRecord:save/update不保存记录,即使它说保存了,ruby-on-rails,ruby,postgresql,activerecord,ruby-on-rails-5,Ruby On Rails,Ruby,Postgresql,Activerecord,Ruby On Rails 5,在ruby 2.6.5、postgres 10.11和pg gem 1.1.4版的Rails 5.2.4.1应用程序中,我有一个如下的ActiveRecord模型: class房地产{select(RealEstate.column_names.map!(&:to_sym)-[:metadata]) 作用域:活动,->{where(活动:true)} 有_many:lots,->{where(lot:true)},类名:'realstate',外键:'program\u id' 属于:程序,类名

在ruby 2.6.5、postgres 10.11和pg gem 1.1.4版的Rails 5.2.4.1应用程序中,我有一个如下的ActiveRecord模型:

class房地产{select(RealEstate.column_names.map!(&:to_sym)-[:metadata])
作用域:活动,->{where(活动:true)}
有_many:lots,->{where(lot:true)},类名:'realstate',外键:'program\u id'
属于:程序,类名:'realstate',可选:true
枚举旧的或新的:{
老:0,,
新#U大厦:1#开发商新房地产项目
}
枚举事务类型:{
租金:0,,
购买:1
}
枚举属性\u类别:{
公寓:0,
众议院:1,
其他:2
}
结束
db/structure.sql
中的底层数据模型如下(某些列匿名):

正如您所看到的,没有Rails回调。“奇怪之处”——我们使用一个postgres JSONB列和一个postgres integer数组列

现在我们有了一个管理员更新路径来编辑其中的一些记录。它不遵循命名约定,也不使用活动记录验证,而是使用干式验证:

require 'dry-validation'

module Admin
  class NewRealEstatesController < Admin::BaseController
    before_action :set_form, only: [:edit, :update]

    def update
      validation_result = AdminRealEstateSchema.call(real_estate_listing_params.to_h)
      if validation_result.success?
        # Don't mind column mappings with the dry schema validator, there might be inconsistencies
       # because I anonymized the column names, but the mapping is correct and validations pass
        @estate.some_string_1 =  params[:real_estate][:some_string_1]
        @estate.some_string_2 = params[:real_estate][:some_string_2]
        @estate.some_string_3 = params[:real_estate][:some_string_3]
        @estate.property_category = params[:real_estate][:property_category]
        @estate.some_string_4 = params[:real_estate][:some_string_4]
        @estate.price = params[:real_estate][:price]
        @estate.some_string_5 = params[:real_estate][:some_string_5]
        @estate.active = params[:real_estate][:active]
        @estate.some_timestamp_1 = params[:real_estate][:some_timestamp_1]
        @estate.some_timestamp_2 = params[:real_estate][:some_timestamp_2]
        @estate.some_count = params[:real_estate][:some_count]
        @estate.surface = params[:real_estate][:surface]
        @estate.some_boolean_1 = params[:real_estate][:some_boolean_1]
        @estate.some_boolean_2 = params[:real_estate][:some_boolean_2]
        res = @estate.save
        render json: { result: 'done', debug: { res_save_ar: res, changed: @estate.saved_changes?, \
          changes: @estate.saved_changes, previous_changes: @estate.previous_changes } }, status: 200
      else
        respond_to do |format|
          format.html { render :edit }
          format.json { render json: { errors: validation_result.errors.map{|key, error| "#{key} : #{error[0]}"} },  \
            status: :unprocessable_entity, serializer: nil }
        end
      end
    end

    def edit; end

    private 

    def set_form
      @estate = RealEstate.find(params[:id])
    end

    def real_estate_listing_params
      params[:real_estate_listing].permit(...) # all strong params edited in route, it's correct
    end
  end
end

AdminRealEstateSchema = Dry::Validation.Form do
  configure { config.type_specs = true, config.messages = :i18n }
  PROPERTY_CATEGORIES = %w(house apartment other)
  required(:property_category, :string).filled(:str?, included_in?: PROPERTY_CATEGORIES)
  required(:some_string_1, :string).filled(:str?)
  required(:some_string_2, :string).filled
  optional(:some_string_3).maybe(:str?)
  optional(:some_string_4).maybe(:str?)
  optional(:some_string_4).maybe(:str?)
  optional(:some_string_5).maybe(:str?)
  required(:price, :int).filled(:int?)
  optional(:some_other_int).maybe(:int?)
  optional(:some_count).maybe(:int?)
  required(:active, :bool).filled(:bool?)
  required(:some_boolean_1, :bool).filled(:bool?)
  required(:some_boolean_2, :bool).filled(:bool?)
  required(:some_time_1, :date_time).filled(:date_time?)
  optional(:some_time_2).maybe(:date_time?)
end
要求“干式验证”
模块管理员
类NewRealEstatesController
视图是正确的,使用
remote:true

现在谈谈这个问题:有时,由于表单的存在,记录会被保存和更新,但没有一致性,有时绝对不会保存和更新,就像更改已被回滚一样,即使
save
返回true,如web控制台中返回的JSON所示

ActiveRecord还可以正确地映射所有应使用正确值更新/保存的列,如
@estate.saved\u changes

Rspec测试全部通过。但只有在生产中,我们才会遇到这种错误

现在需要注意的几点可能很重要:

  • 几个月前,我们已经遇到了一个
    create\u或
    bug。无法正确创建记录。迫于最后期限,我通过
    ActiveRecord::Base.connection.execute
    求助于原始SQL,结果一切正常
  • 此表非常活跃,有许多后台作业插入/读取客户的数据
  • 此表上没有自定义postgres触发器
我越来越绝望了,我想这可能是我们代码中的一个bug(很明显),或者是
activerecord
中的一个bug


有什么想法吗

我相信这可能与最新稳定版本中的一个已知错误有关。我会和其他一些与pg开放相关的问题一起检查。我在使用pg数据类型时遇到了类似的情况,这些数据类型不是rails默认的int和string值(使用JSONB和Money)。我们是n
require 'dry-validation'

module Admin
  class NewRealEstatesController < Admin::BaseController
    before_action :set_form, only: [:edit, :update]

    def update
      validation_result = AdminRealEstateSchema.call(real_estate_listing_params.to_h)
      if validation_result.success?
        # Don't mind column mappings with the dry schema validator, there might be inconsistencies
       # because I anonymized the column names, but the mapping is correct and validations pass
        @estate.some_string_1 =  params[:real_estate][:some_string_1]
        @estate.some_string_2 = params[:real_estate][:some_string_2]
        @estate.some_string_3 = params[:real_estate][:some_string_3]
        @estate.property_category = params[:real_estate][:property_category]
        @estate.some_string_4 = params[:real_estate][:some_string_4]
        @estate.price = params[:real_estate][:price]
        @estate.some_string_5 = params[:real_estate][:some_string_5]
        @estate.active = params[:real_estate][:active]
        @estate.some_timestamp_1 = params[:real_estate][:some_timestamp_1]
        @estate.some_timestamp_2 = params[:real_estate][:some_timestamp_2]
        @estate.some_count = params[:real_estate][:some_count]
        @estate.surface = params[:real_estate][:surface]
        @estate.some_boolean_1 = params[:real_estate][:some_boolean_1]
        @estate.some_boolean_2 = params[:real_estate][:some_boolean_2]
        res = @estate.save
        render json: { result: 'done', debug: { res_save_ar: res, changed: @estate.saved_changes?, \
          changes: @estate.saved_changes, previous_changes: @estate.previous_changes } }, status: 200
      else
        respond_to do |format|
          format.html { render :edit }
          format.json { render json: { errors: validation_result.errors.map{|key, error| "#{key} : #{error[0]}"} },  \
            status: :unprocessable_entity, serializer: nil }
        end
      end
    end

    def edit; end

    private 

    def set_form
      @estate = RealEstate.find(params[:id])
    end

    def real_estate_listing_params
      params[:real_estate_listing].permit(...) # all strong params edited in route, it's correct
    end
  end
end

AdminRealEstateSchema = Dry::Validation.Form do
  configure { config.type_specs = true, config.messages = :i18n }
  PROPERTY_CATEGORIES = %w(house apartment other)
  required(:property_category, :string).filled(:str?, included_in?: PROPERTY_CATEGORIES)
  required(:some_string_1, :string).filled(:str?)
  required(:some_string_2, :string).filled
  optional(:some_string_3).maybe(:str?)
  optional(:some_string_4).maybe(:str?)
  optional(:some_string_4).maybe(:str?)
  optional(:some_string_5).maybe(:str?)
  required(:price, :int).filled(:int?)
  optional(:some_other_int).maybe(:int?)
  optional(:some_count).maybe(:int?)
  required(:active, :bool).filled(:bool?)
  required(:some_boolean_1, :bool).filled(:bool?)
  required(:some_boolean_2, :bool).filled(:bool?)
  required(:some_time_1, :date_time).filled(:date_time?)
  optional(:some_time_2).maybe(:date_time?)
end