#ruby-on-rails #ruby #postgresql #activerecord
#ruby-on-rails #ruby #postgresql #activerecord
Вопрос:
У меня проблема с отношением «has_many», когда я получаю случайное исключение из ActiveRecord:
product = Product.create!(valid_attributes)
product.prices
# throws:
NoMethodError:
undefined method `scan' for nil:NilClass
Похоже, это связано с «inverse_of», но я, по-видимому, сделал что-то, чего ActiveRecord не ожидал, но не мог побеспокоиться о том, чтобы иметь хорошую ошибку для. Лучше всего предположить, что это как-то связано с моим столбцом с именем «column» (хотя его нет в черном списке AFAIK). Я использую PostgreSQL. РЕДАКТИРОВАТЬ: попробовал переименовать столбец в «column_name» и «parent_column», и это ничего не исправило. Попробую сделать кое-что другое.
Вот соответствующий код модели и схема:
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
И полной трассировкой стека:
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'
Комментарии:
1. Не могли бы вы попробовать переименовать этот столбец, просто чтобы убедиться, проблема это или нет?
2. разве
column
это не зарезервированное слово любой используемой вами СУБД?. может быть, в этом проблема ?, не уверен. Попробуйте изменить его, как предложил @arieljuod.3. dev.mysql.com/doc/refman/5.7/en/…
4. Я попытался переименовать его, и безуспешно. Я попробую еще раз на случай, если он не «принял». Я использую PostgreSQL, а не MySQL, но спасибо за идею. СТОЛБЕЦ также указан как зарезервированное слово в PG.
5. Возможно, я решил проблему, мне будет неловко, если это то, что я думаю.
Ответ №1:
Ответ заключался в том, что я нарушал ожидания ActiveRecord способом, которого я не осознавал, и должен был включить в исходный вопрос, но не сделал этого из-за высокомерия. Я использовал анонимную модель в тестировании. Например:
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
Это позволяет мне использовать модель ActiveRecord с существующей таблицей, но отделить ее от всей логики в реальной Product
модели.
Я никогда раньше не сталкивался с какими-либо проблемами с этой настройкой, поэтому я с удовольствием использую ее для тестирования модулей в течение многих лет. Итак, моя вина; если бы я поместил этот код в исходный вопрос, я, вероятно, понял бы ответ перед публикацией, но я отвечаю на свой собственный вопрос в качестве предупреждения другим «умным» программистам Ruby.