ruby on rails - पॉलिमॉर्फिक एसोसिएशन एसटीआई के लिए क्यों काम नहीं करता है यदि पॉलिमॉर्फिक एसोसिएशन का टाइप कॉलम एसटीआई के बेस मॉडल को इंगित नहीं करता है?




ruby-on-rails activerecord (4)

मेरे पास पॉलिमॉर्फिक एसोसिएशन और एसटीआई का मामला है।

# app/models/car.rb
class Car < ActiveRecord::Base
  belongs_to :borrowable, :polymorphic => true
end

# app/models/staff.rb
class Staff < ActiveRecord::Base
  has_one :car, :as => :borrowable, :dependent => :destroy
end

# app/models/guard.rb
class Guard < Staff
end

borrowable_type पर एपीआई दस्तावेज के अनुसार, borrowable_type लिए borrowable_type करने के लिए, http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations जिन्हें मुझे borrowable_type सेट borrowable_type है एसटीआई मॉडल का base_class , जो मेरे मामले में है Staff

सवाल यह है: यदि यह borrowable_type एसटीआई कक्षा में सेट है तो यह क्यों काम नहीं करता है?

साबित करने के लिए कुछ परीक्षण:

# now the test speaks only truth

# test/fixtures/cars.yml
one:
  name: Enzo
  borrowable: staff (Staff)

two:
  name: Mustang
  borrowable: guard (Guard)

# test/fixtures/staffs.yml
staff:
  name: Jullia Gillard

guard:
  name: Joni Bravo
  type: Guard 

# test/units/car_test.rb

require 'test_helper'

class CarTest < ActiveSupport::TestCase
  setup do
    @staff = staffs(:staff)
    @guard = staffs(:guard) 
  end

  test "should be destroyed if an associated staff is destroyed" do
    assert_difference('Car.count', -1) do
      @staff.destroy
    end
  end

  test "should be destroyed if an associated guard is destroyed" do
    assert_difference('Car.count', -1) do
      @guard.destroy
    end
  end

end

लेकिन यह केवल स्टाफ के उदाहरण के साथ सच है। परिणाम हैं:

# Running tests:

F.

Finished tests in 0.146657s, 13.6373 tests/s, 13.6373 assertions/s.

  1) Failure:
test_should_be_destroyed_if_an_associated_guard_is_destroyed(CarTest) [/private/tmp/guineapig/test/unit/car_test.rb:16]:
"Car.count" didn't change by -1.
<1> expected but was
<2>.

धन्यवाद


अच्छा प्रश्न। रेल 3.1 का उपयोग कर मुझे बिल्कुल वही समस्या थी। ऐसा लगता है कि आप ऐसा नहीं कर सकते हैं, क्योंकि यह काम नहीं करता है। शायद यह एक इरादा व्यवहार है। जाहिर है, रेल में सिंगल टेबल विरासत (एसटीआई) के साथ संयोजन में पॉलीमोर्फिक एसोसिएशन का उपयोग करना थोड़ा जटिल है।

रेल 3.2 के लिए वर्तमान रेल दस्तावेज http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations संयोजन के लिए यह सलाह देता है:

सिंगल टेबल विरासत (एसटीआई) के साथ संयोजन में पॉलीमोर्फिक एसोसिएशन का उपयोग करना थोड़ा मुश्किल है। एसोसिएशन को उम्मीद के अनुसार काम करने के लिए, सुनिश्चित करें कि आप पॉलिमॉर्फिक एसोसिएशन के प्रकार कॉलम में एसटीआई मॉडल के लिए बेस मॉडल स्टोर करते हैं।

आपके मामले में आधार मॉडल "कर्मचारी" होगा, यानी "उधार योग्य_ प्रकार" सभी वस्तुओं के लिए "कर्मचारी" होना चाहिए, न कि "गार्ड"। "बनने" का उपयोग करके व्युत्पन्न वर्ग बेस क्लास के रूप में दिखाई देना संभव है: guard.becomes(Staff) । कोई भी "उधार योग्य_ प्रकार" कॉलम को सीधे बेस क्लास "कर्मचारी" पर सेट कर सकता है, या रेल दस्तावेज़ीकरण के सुझाव के अनुसार, इसे स्वचालित रूप से उपयोग करके परिवर्तित करें

class Car < ActiveRecord::Base
  ..
  def borrowable_type=(sType)
     super(sType.to_s.classify.constantize.base_class.to_s)
  end

एक पुराना सवाल है, लेकिन रेल 4 में मुद्दा अभी भी बना हुआ है। एक अन्य विकल्प एक प्रकार के साथ _type विधि गतिशील रूप से बनाने / ओवरराइट करना है। यह उपयोगी होगा यदि आपका ऐप एसटीआई के साथ कई पॉलिमॉर्फिक एसोसिएशन का उपयोग करता है और आप तर्क को एक ही स्थान पर रखना चाहते हैं।

यह चिंता सभी पॉलिमॉर्फिक संघों को पकड़ लेगी और यह सुनिश्चित करेगी कि रिकॉर्ड हमेशा बेस क्लास का उपयोग करके सहेजा जाता है।

# models/concerns/single_table_polymorphic.rb
module SingleTablePolymorphic
  extend ActiveSupport::Concern

  included do
    self.reflect_on_all_associations.select{|a| a.options[:polymorphic]}.map(&:name).each do |name|
      define_method "#{name.to_s}_type=" do |class_name|
        super(class_name.constantize.base_class.name)
      end
    end
  end
end

फिर बस इसे अपने मॉडल में शामिल करें:

class Car < ActiveRecord::Base
  belongs_to :borrowable, :polymorphic => true
  include SingleTablePolymorphic
end

मैं सामान्य टिप्पणियों से सहमत हूं कि यह आसान होना चाहिए। उसने कहा, यहाँ मेरे लिए क्या काम किया है।

मेरे पास फर्म के साथ बेस क्लास और ग्राहक और प्रॉस्पेक्ट के रूप में एसटीआई कक्षाओं के रूप में एक मॉडल है, जैसा कि:

class Firm
end

class Customer < Firm
end

class Prospect < Firm
end

मेरे पास एक पॉलिमॉर्फिक क्लास, अवसर भी है, जो इस तरह दिखता है:

class Opportunity
  belongs_to :opportunistic, polymorphic: true
end

मैं अवसरों को संदर्भित करना चाहता हूं

customer.opportunities

या

prospect.opportunities

ऐसा करने के लिए मैंने मॉडल को निम्नानुसार बदल दिया।

class Firm
  has_many opportunities, as: :opportunistic
end

class Opportunity
  belongs_to :customer, class_name: 'Firm', foreign_key: :opportunistic_id
  belongs_to :prospect, class_name: 'Firm', foreign_key: :opportunistic_id
end

मैं 'फर्म' (बेस क्लास) और संबंधित ग्राहक या संभावित आईडी के अवसरवादी_आईडी के अवसरों के साथ अवसरों को बचाता हूं।

अब मैं ग्राहक को प्राप्त कर सकता हूं। अवसर और संभावनाएं। जैसे ही मैं चाहता हूं।


Rails 4.2 में बस यह मुद्दा था। मुझे हल करने के दो तरीके मिले:

-

समस्या यह है कि रेल एसटीआई रिश्ते के base_class नाम का उपयोग करता है।

इसका कारण अन्य उत्तरों में दस्तावेज किया गया है, लेकिन यह बात यह है कि कोर टीम को लगता है कि आपको पॉलिमॉर्फिक एसटीआई एसोसिएशन के लिए कक्षा के बजाय तालिका का संदर्भ देने में सक्षम होना चाहिए।

मैं इस विचार से असहमत हूं, लेकिन रेल कोर टीम का हिस्सा नहीं हूं, इसलिए इसे हल करने में ज्यादा इनपुट नहीं है।

इसे ठीक करने के दो तरीके हैं:

-

1) मॉडल स्तर पर डालें:

class Association < ActiveRecord::Base

  belongs_to :associatiable, polymorphic: true
  belongs_to :associated, polymorphic: true

  before_validation :set_type

  def set_type
    self.associated_type = associated.class.name
  end
end

यह डीबी में डेटा के निर्माण से पहले {x}_type रिकॉर्ड बदल देगा। यह बहुत अच्छी तरह से काम करता है, और अभी भी एसोसिएशन की बहुलक प्रकृति को बरकरार रखता है।

2) कोर ActiveRecord विधियों को ओवरराइड करें

#app/config/initializers/sti_base.rb
require "active_record"
require "active_record_extension"
ActiveRecord::Base.store_base_sti_class = false

#lib/active_record_extension.rb
module ActiveRecordExtension #-> http://.com/questions/2328984/rails-extending-activerecordbase

  extend ActiveSupport::Concern

  included do
    class_attribute :store_base_sti_class
    self.store_base_sti_class = true
  end
end

# include the extension 
ActiveRecord::Base.send(:include, ActiveRecordExtension)

####

module AddPolymorphic
  extend ActiveSupport::Concern

  included do #-> http://.com/questions/28214874/overriding-methods-in-an-activesupportconcern-module-which-are-defined-by-a-cl
    define_method :replace_keys do |record=nil|
      super(record)
      owner[reflection.foreign_type] = ActiveRecord::Base.store_base_sti_class ? record.class.base_class.name : record.class.name
    end
  end
end

ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, AddPolymorphic)

समस्या को ठीक करने का एक और व्यवस्थित तरीका ActiveRecord कोर विधियों को संपादित करना है जो इसे नियंत्रित करते हैं। मैंने इस मणि में संदर्भों का उपयोग यह पता लगाने के लिए किया कि कौन से तत्वों को निश्चित / ओवरराइड करने की आवश्यकता है।

यह अनचाहे है और अभी भी ActiveRecord कोर विधियों के कुछ अन्य हिस्सों के लिए एक्सटेंशन की आवश्यकता है, लेकिन मेरे स्थानीय सिस्टम के लिए काम करना प्रतीत होता है।





polymorphic-associations