简体   繁体   English

与:inverse_of的多态ActiveRecord关联的“未定义方法'scan'for nil:NilClass”

[英]“undefined method `scan' for nil:NilClass” for polymorphic ActiveRecord association with :inverse_of

I have an issue with a "has_many" relationship, where I am getting a random exception from within ActiveRecord: 我有一个“ has_many”关系问题,我从ActiveRecord中得到一个随机异常:

product = Product.create!(valid_attributes)
product.prices 
# throws:
NoMethodError:
       undefined method `scan' for nil:NilClass

This appears to have to do with "inverse_of", but I've apparently done something that ActiveRecord didn't expect, but couldn't be bothered to have a good error for. 这似乎与“ inverse_of”有关,但是我显然已经做了ActiveRecord没想到的事情,但是也不会因为出现一个好的错误而烦恼。 Best guess is it has something to do with my column named "column" (although that isn't on the blacklist AFAIK). 最好的猜测是它与我的名为“ column”的列有关(尽管不在AFAIK黑名单中)。 I'm using PostgreSQL. 我正在使用PostgreSQL。 EDIT: tried renaming the column to "column_name" and "parent_column", and that didn't fix it. 编辑:尝试将列重命名为“ column_name”和“ parent_column”,但并没有解决。 Will try some other things. 会尝试其他一些东西。

Here is the relevant model code and schema: 以下是相关的模型代码和模式:

class Price < ApplicationRecord
  belongs_to :parent, polymorphic: true
end

class Product < ApplicationRecord
  has_many :prices, as: :parent, inverse_of: :parent
end

class CreatePrices < ActiveRecord::Migration[5.2]
  def change
    create_table :prices do |t|
      t.string :parent_type, null: false
      t.bigint :parent_id, null: false
      t.string :column, null: false
      t.decimal :price, null: false, precision: 15, scale: 2
      t.timestamp :effective_date, null: false
    end

    add_index :prices, [:parent_type, :parent_id, :column]
  end
end

And the full stacktrace: 以及完整的堆栈跟踪:

NoMethodError:
       undefined method `scan' for nil:NilClass
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/inheritance.rb:185:in `compute_type'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:422:in `compute_class'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:379:in `klass'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:234:in `inverse_of'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:239:in `check_validity_of_inverse!'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:474:in `check_validity!'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations/association.rb:26:in `initialize'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations.rb:237:in `new'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations.rb:237:in `association'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations/builder/association.rb:108:in `prices'

The answer was that I was violating ActiveRecord's expectations in a way that I didn't realize, and should have included in the original question, but did not due to hubris. 答案是,我以某种我没有意识到的方式违反了ActiveRecord的期望,并且本来应该包含在原始问题中,但并非出于自负。 I was using an anonymous model in testing. 我在测试中使用匿名模型。 For example: 例如:

module Priced
  extend ActiveSupport::Concern

  included do
    has_many :prices, as: :parent, inverse_of: :parent
  end
end

RSpec.describe Priced do
  let(:model_class) {
    Class.new(ActiveRecord::Base).tap do |klass|
      klass.table_name = "products"
      klass.send(:include, Priced)
    end
  end

  it "defines a 'has_many :prices' association" do
    model = model_class.create!
    price = Price.create!(parent: model, price: 100)
    expect(model.prices).to eq([price])
  end
end

This lets me use an ActiveRecord model with an existing table, but de-couple it from all the logic in the actual Product model. 这使我可以将ActiveRecord模型与现有表一起使用,但可以将其与实际Product模型中的所有逻辑分离。

I had never encountered any issues with this setup before, so I've been using it happily for testing modules for years. 我以前从未遇到过这种设置的任何问题,因此多年来我一直很乐意将其用于测试模块。 So mea culpa; 因此,我是罪魁祸首; if I had put this code in the original question, I probably would have realized the answer before posting, but I'm answering my own question as a warning to other "clever" Ruby coders. 如果我将这段代码放在原始问题中,那么我可能会在发布之前就已经意识到了答案,但是我在回答自己的问题是为了警告其他“聪明的” Ruby编码人员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM