through single references rails polymorphic has_one has_many has_and_belongs_to_many active ruby-on-rails activerecord polymorphic-associations

ruby-on-rails - single - rails references



¿Por qué la asociación polimórfica no funciona para STI si la columna tipo de la asociación polimórfica no apunta al modelo base de STI? (5)

Aquí tengo un caso de asociación polimórfica e ITS.

# 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

Para que funcione la asociación polimórfica, de acuerdo con la documentación de la API sobre Asociación polimórfica, http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations que tengo que establecer borrowable_type para la base_class de los modelos de STI, en mi caso es Staff .

La pregunta es: ¿por qué no funciona si el tipo de borrowable_type establece en la clase de STI?

Algunas pruebas para probarlo:

# 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

Pero parece ser cierto solo con la instancia Staff . Los resultados son:

# 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>.

Gracias


Acabo de tener este problema en Rails 4.2 . Encontré dos formas de resolver:

-

El problema es que Rails usa el nombre de base_class de la relación de STI.

La razón de esto se ha documentado en las otras respuestas, pero la esencia es que el equipo central parece sentir que debería ser capaz de hacer referencia a la tabla en lugar de a la clase de una asociación polimérica de ITS.

No estoy de acuerdo con esta idea, pero no soy parte del equipo de Rails Core, por lo que no tengo mucha información para resolverlo.

Hay dos formas de solucionarlo:

-

1) Insertar a nivel de modelo:

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

Esto cambiará el registro {x}_type antes de la creación de los datos en el db. Esto funciona muy bien, y aún conserva la naturaleza polimórfica de la asociación.

2) Reemplazar los métodos Core 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)

Una forma más sistémica de solucionar el problema es editar los métodos centrales de ActiveRecord que lo rigen. Usé referencias en esta gema para descubrir qué elementos necesitaban ser reparados / reemplazados.

Esto no se ha probado y aún necesita extensiones para algunas de las otras partes de los métodos básicos de ActiveRecord, pero parece funcionar para mi sistema local.


Buena pregunta. Tenía exactamente el mismo problema con Rails 3.1. Parece que no puedes hacer esto, porque no funciona. Probablemente es un comportamiento intencionado. Aparentemente, usar asociaciones polimórficas en combinación con herencia de tabla única (ITS) en Rails es un poco complicado.

La documentación actual de Rails para Rails 3.2 brinda este consejo para combinar http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations :

Usar asociaciones polimórficas en combinación con la herencia de tablas únicas (STI) es un poco complicado. Para que las asociaciones funcionen como se esperaba, asegúrese de almacenar el modelo base para los modelos de STI en la columna de tipo de la asociación polimórfica.

En su caso, el modelo base sería "Staff", es decir, "borrowable_type" debería ser "Staff" para todos los artículos, no "Guard". Es posible hacer que la clase derivada aparezca como la clase base usando "se convierte": guard.becomes(Staff) . Se podría establecer la columna "borrowable_type" directamente en la clase base "Staff", o como sugiere la documentación de Rails, convertirla automáticamente usando

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


Estoy de acuerdo con los comentarios generales de que esto debería ser más fácil. Dicho eso, esto es lo que funcionó para mí.

Tengo un modelo con Firma como clase base y Cliente y Prospect como las clases de STI, así:

class Firm end class Customer < Firm end class Prospect < Firm end

También tengo una clase polimórfica, Opportunity, que se ve así:

class Opportunity belongs_to :opportunistic, polymorphic: true end

Quiero referirme a las oportunidades como

customer.opportunities

o

prospect.opportunities

Para hacer eso, cambié los modelos de la siguiente manera.

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

Ahorro oportunidades con un oportunistic_type de ''Firm'' (la clase base) y el cliente respectivo o el ID de cliente potencial como el opportunistic_id.

Ahora puedo obtener oportunidades de cliente y oportunidades potenciales exactamente como yo quiero.



Una pregunta anterior, pero el problema en Rails 4 aún permanece. Otra opción es crear / sobreescribir dinámicamente el método _type con una preocupación. Esto sería útil si su aplicación utiliza múltiples asociaciones polimórficas con STI y desea mantener la lógica en un solo lugar.

Esta preocupación atrapará todas las asociaciones polimórficas y asegurará que el registro siempre se guarde utilizando la clase base.

# 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

Luego solo inclúyelo en tu modelo:

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