Rails 7使用PORO处理第三方API数据时出现未知属性错误
问题描述
从NHL API获取的比赛数据结构如下:
{:gamePk=>2023010045, :link=>"/api/v1/game/2023010045/feed/live", :gameType=>"PR", :season=>"20232024", :gameDate=>"2023-09-29T00:30:00Z", :status=>{:abstractGameState=>"Preview", :codedGameState=>"1", :detailedState=>"Scheduled", :statusCode=>"1", :startTimeTBD=>false}, :teams=> {:away=>{:leagueRecord=>{:wins=>0, :losses=>0, :type=>"league"}, :score=>0, :team=>{:id=>19, :name=>"St. Louis Blues", :link=>"/api/v1/teams/19"}}, :home=>{:leagueRecord=>{:wins=>0, :losses=>0, :type=>"league"}, :score=>0, :team=>{:id=>16, :name=>"Chicago Blackhawks", :link=>"/api/v1/teams/16"}}}, :venue=>{:id=>5092, :name=>"United Center", :link=>"/api/v1/venues/5092"}, :content=>{:link=>"/api/v1/game/2023010045/content"}}
为了将数据转换为PORO,编写了以下Game类:
class Game attr_reader :start_time, :home_team, :away_team def initialize(data) @start_time = data[:gameDate].to_datetime.strftime("%F") @home_team = data[:teams][:home][:team][:name] @away_team = data[:teams][:away][:team][:name] end end
但执行Game.new(game)时,会抛出以下错误之一:
eval error: unknown attribute 'gamePk' for Game.
或
ActiveModel::UnknownAttributeError (unknown attribute 'gamePk' for Game.
错误回溯信息:
eval error: unknown attribute 'gamePk' for Game. raise UnknownAttributeError.new(self, k.to_s) ^^^^^ /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activemodel-7.0.7/lib/active_model/attribute_assignment.rb:51:in `_assign_attribute' /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activerecord-7.0.7/lib/active_record/attribute_assignment.rb:21:in `block in _assign_attributes' /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activerecord-7.0.7/lib/active_record/attribute_assignment.rb:13:in `each' /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activerecord-7.0.7/lib/active_record/attribute_assignment.rb:13:in `_assign_attributes' /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activemodel-7.0.7/lib/active_model/attribute_assignment.rb:34:in `assign_attributes' /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activerecord-7.0.7/lib/active_record/core.rb:468:in `initialize' /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activerecord-7.0.7/lib/active_record/inheritance.rb:75:in `new' /Users/jrbobbdobbs/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activerecord-7.0.7/lib/active_record/inheritance.rb:75:in `new' (rdbg)//Users/jrbobbdobbs/Documents/projects/sites/hockey/ChicagoBlackhawks/app/facades/game_facade.rb:1:in `block in create_games'
核心疑问:为何Game类未引用:gamePk字段,Rails仍会抛出该字段的未知属性错误?
原因分析
你的Game类被Rails识别成了ActiveRecord模型类,导致调用Game.new(game)时,实际执行的是ActiveRecord的初始化逻辑,而非你自定义的initialize方法。
从错误回溯的调用链可以看到,代码走到了active_record/core.rb的initialize方法,而非你编写的初始化逻辑。触发这个问题的常见场景:
Game类存放在app/models目录下,Rails默认会自动让该目录下的类继承ActiveRecord::Base(即使你没显式写继承代码)- 你不小心让
Game类继承了ActiveRecord::Base,触发了ActiveRecord的属性赋值机制——它会尝试将传入哈希中的所有键(包括:gamePk)作为模型属性赋值,但你的类并未定义这些属性,因此抛出错误。
解决方案
提供三种可行的解决方式:
确保类为纯Ruby对象
显式定义不继承任何ActiveRecord相关类的纯Ruby类,避免Rails自动添加模型特性:class Game attr_reader :start_time, :home_team, :away_team def initialize(data) @start_time = data[:gameDate].to_datetime.strftime("%F") @home_team = data[:teams][:home][:team][:name] @away_team = data[:teams][:away][:team][:name] end end调整类的存放目录
如果Game类当前在app/models目录下,将其移到非模型目录,比如app/poros、app/services等,Rails不会为这些目录下的类自动添加ActiveRecord继承。标记为抽象类(需留在models目录时)
如果必须将Game类放在app/models目录,可将其标记为抽象类,阻止ActiveRecord的属性赋值逻辑:class Game < ActiveRecord::Base self.abstract_class = true attr_reader :start_time, :home_team, :away_team def initialize(data) @start_time = data[:gameDate].to_datetime.strftime("%F") @home_team = data[:teams][:home][:team][:name] @away_team = data[:teams][:away][:team][:name] end end
内容的提问来源于stack exchange,提问作者J.R. Bob Dobbs
相关产品推荐
相关产品推荐

