Rails i18n API存入MySQL值格式异常致find_or_create_by失效求助
解决Rails i18n数据库存储value字段自动YAML序列化导致的重复数据问题
这个问题我之前也碰到过!本质原因是Rails i18n的数据库存储实现(比如常用的i18n-active_record gem)默认会把value字段做YAML序列化——因为i18n支持复数、数组甚至嵌套结构的翻译内容,用YAML序列化存储是默认行为。这就导致你直接传入字符串'value1'时,会被自动转成YAML格式的字符串:--- value1\n...\n,自然用原始字符串查询时匹配不到,进而重复创建记录。
下面给你几个可行的解决方案,按需选择:
方案1:禁用value字段的自动序列化(适合纯文本翻译场景)
如果你的翻译内容都是纯字符串,不需要复数、数组等复杂结构,可以直接修改Translation模型,去掉序列化声明:
class Translation < ApplicationRecord # 删掉这一行:serialize :value # 其他原有代码保留 end
这样修改后,后续写入的value会直接以原始字符串存储,find_or_create_by就能正常匹配字段值,不会再产生重复数据。如果已经有存量数据,你可以写个迁移脚本把现有YAML格式的value转成纯文本:
# 示例迁移脚本 class FixTranslationValues < ActiveRecord::Migration[7.0] def up Translation.find_each do |t| parsed_value = YAML.load(t.value) t.update_column(:value, parsed_value) unless parsed_value.nil? end end end
方案2:保留序列化,自定义查询匹配逻辑(适合需要复杂翻译结构的场景)
如果你的项目需要支持复数、数组等翻译格式,不能禁用序列化,那可以自定义一个方法来替代原生的find_or_create_by,实现基于反序列化后的内容匹配:
class Translation < ApplicationRecord serialize :value def self.find_or_create_by_translation(locale, key, value) # 遍历同locale同key的记录,反序列化后匹配value existing_record = where(locale: locale, key: key).find do |record| YAML.load(record.value) == value end # 找不到则创建新记录 existing_record || create(locale: locale, key: key, value: value) end end
之后调用这个方法来创建或查询:
Translation.find_or_create_by_translation('en', 'key1', 'value1')
方案3:提前序列化查询值(快速临时解决方案)
如果只是临时需要解决重复创建的问题,也可以在调用find_or_create_by时,先把要匹配的value手动序列化成YAML格式:
require 'yaml' serialized_value = YAML.dump('value1') Translation.find_or_create_by(locale: 'en', key:'key1', value: serialized_value)
不过这个方法需要每次手动处理序列化,长期用起来比较繁琐,更适合临时修复场景。
内容的提问来源于stack exchange,提问作者Melih
相关产品推荐
相关产品推荐

