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

Rails 6.1升级时load_defaults与框架默认文件行为不一致问题

Rails 6.0升级到6.1测试用例出现数据库约束错误求助

将项目从Rails 6.0升级到6.1时遇到以下问题:

  • 初始状态:application.rb中配置config.load_defaults = 6.0,保留config/initializers/new_framework_defaults_6_1.rb文件,所有测试用例执行正常
  • 修改后:移除new_framework_defaults_6_1.rb,在application.rb中添加环境判断切换为config.load_defaults 6.1,大量测试失败,报错集中在ActiveRecord::RecordNotUnique(违反联合唯一约束)和ActiveRecord::NotNullViolation(违反非空约束)两类数据库错误,无法继续推进升级,求有类似经历的开发者提供解决思路。

项目配置文件内容

config/initializers/new_framework_defaults_6_0.rb

# Be sure to restart your server when you modify this file.
#
# This file contains migration options to ease your Rails 6.0 upgrade.
#
# Once upgraded flip defaults one by one to migrate to the new default.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.

# Don't force requests from old versions of IE to be UTF-8 encoded.
Rails.application.config.action_view.default_enforce_utf8 = false

# Embed purpose and expiry metadata inside signed and encrypted
# cookies for increased security.
#
# This option is not backwards compatible with earlier Rails versions.
# It's best enabled when your entire app is migrated and stable on 6.0.
Rails.application.config.action_dispatch.use_cookies_with_metadata = true

# Change the return value of `ActionDispatch::Response#content_type` to Content-Type header without modification.
# Rails.application.config.action_dispatch.return_only_media_type_on_content_type = false

# Return false instead of self when enqueuing is aborted from a callback.
# Rails.application.config.active_job.return_false_on_aborted_enqueue = true

# Send Active Storage analysis and purge jobs to dedicated queues.
# Rails.application.config.active_storage.queues.analysis = :active_storage_analysis
# Rails.application.config.active_storage.queues.purge    = :active_storage_purge

# When assigning to a collection of attachments declared via `has_many_attached`, replace existing
# attachments instead of appending. Use #attach to add new attachments without replacing existing ones.
Rails.application.config.active_storage.replace_on_assign_to_many = true

# Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail.
#
# The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob),
# will be removed in Rails 6.1. This setting is not backwards compatible with earlier Rails versions.
# If you send mail in the background, job workers need to have a copy of
# MailDeliveryJob to ensure all delivery jobs are processed properly.
# Make sure your entire app is migrated and stable on 6.0 before using this setting.
# Rails.application.config.action_mailer.delivery_job = 'ActionMailer::MailDeliveryJob'

# Enable the same cache key to be reused when the object being cached of type
# `ActiveRecord::Relation` changes by moving the volatile information (max updated at and count)
# of the relation's cache key into the cache version to support recycling cache key.
Rails.application.config.active_record.collection_cache_versioning = true

config/initializers/new_framework_defaults_6_1.rb

# Be sure to restart your server when you modify this file.
#
# This file contains migration options to ease your Rails 6.1 upgrade.
#
# Once upgraded flip defaults one by one to migrate to the new default.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.

# Support for inversing belongs_to -> has_many Active Record associations.
Rails.application.config.active_record.has_many_inversing = true

# Track Active Storage variants in the database.
Rails.application.config.active_storage.track_variants = true

# Apply random variation to the delay when retrying failed jobs.
Rails.application.config.active_job.retry_jitter = 0.15

# Stop executing `after_enqueue`/`after_perform` callbacks if
# `before_enqueue`/`before_perform` respectively halts with `throw :abort`.
Rails.application.config.active_job.skip_after_callbacks_if_terminated = true

# Specify cookies SameSite protection level: either :none, :lax, or :strict.
#
# This change is not backwards compatible with earlier Rails versions.
# It's best enabled when your entire app is migrated and stable on 6.1.
Rails.application.config.action_dispatch.cookies_same_site_protection = :lax

# Generate CSRF tokens that are encoded in URL-safe Base64.
#
# This change is not backwards compatible with earlier Rails versions.
# It's best enabled when your entire app is migrated and stable on 6.1.
Rails.application.config.action_controller.urlsafe_csrf_tokens = true

# Specify whether `ActiveSupport::TimeZone.utc_to_local` returns a time with an
# UTC offset or a UTC time.
ActiveSupport.utc_to_local_returns_utc_offset_times = true

# Change the default HTTP status code to `308` when redirecting non-GET/HEAD
# requests to HTTPS in `ActionDispatch::SSL` middleware.
Rails.application.config.action_dispatch.ssl_default_redirect_status = 308

# Use new connection handling API. For most applications this won't have any
# effect. For applications using multiple databases, this new API provides
# support for granular connection swapping.
Rails.application.config.active_record.legacy_connection_handling = false

# Make `form_with` generate non-remote forms by default.
Rails.application.config.action_view.form_with_generates_remote_forms = false

# Set the default queue name for the analysis job to the queue adapter default.
Rails.application.config.active_storage.queues.analysis = nil

# Set the default queue name for the purge job to the queue adapter default.
Rails.application.config.active_storage.queues.purge = nil

# Set the default queue name for the incineration job to the queue adapter default.
Rails.application.config.action_mailbox.queues.incineration = nil

# Set the default queue name for the routing job to the queue adapter default.
Rails.application.config.action_mailbox.queues.routing = nil

# Set the default queue name for the mail deliver job to the queue adapter default.
Rails.application.config.action_mailer.deliver_later_queue_name = nil

# Generate a `Link` header that gives a hint to modern browsers about
# preloading assets when using `javascript_include_tag` and `stylesheet_link_tag`.
# Rails.application.config.action_view.preload_links_header = true

修改后的application.rb配置片段

if $next_rails
  config.load_defaults 6.1
else
  config.load_defaults 6.0
end

报错信息

RuntimeError: Wrapped undumpable exception for: ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "as_bs_unique"
DETAIL:  Key (a_id, b_id)=(493764392, 897260172) already exists.
Error:
RuntimeError: Wrapped undumpable exception for: ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR:  null value in column "site_id" of relation "xyz_tags" violates not-null constraint
DETAIL:  Failing row contains (16, null, 16).

可能的原因及解决思路

  1. 重点排查has_many_inversing配置
    Rails 6.1默认启用了config.active_record.has_many_inversing = true,这个配置会自动反转belongs_to与has_many的关联,可能导致对象保存时的状态同步逻辑变化。比如之前代码依赖未反转的关联,现在反转后关联对象的属性会自动同步,可能触发重复创建记录或字段未正确赋值的问题。

    • 临时解决方案:在application.rb中显式关闭该配置:config.active_record.has_many_inversing = false,重新运行测试,如果测试恢复正常,再逐个排查受影响的关联代码,调整逻辑适配关联反转的行为。
  2. 分步启用6.1默认配置
    不要直接移除new_framework_defaults_6_1.rb,而是先保留该文件,然后逐个注释掉里面的配置项(即启用默认值),每次只启用一个,运行测试,定位到具体是哪个配置导致的错误,再针对性修复。

  3. 检查测试用例的对象创建逻辑

    • 对于唯一约束错误:查看测试中是否存在重复创建关联记录的逻辑,比如在关联赋值时多次调用create或build,关联反转后可能导致这些操作被重复执行。
    • 对于非空约束错误:检查site_id这类字段的赋值逻辑,是否依赖关联对象的状态同步,关联反转后是否因为对象未正确加载导致字段未赋值。
  4. 验证数据库连接与事务逻辑
    Rails 6.1默认关闭了legacy_connection_handling,虽然大部分项目不受影响,但如果项目使用多数据库或自定义事务逻辑,可能导致连接管理变化,进而影响测试中的数据写入。可以尝试临时开启config.active_record.legacy_connection_handling = true,看是否能解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:17:33