through rails many how has_and_belongs_to_many has foreign example create belongs and active ruby-on-rails polymorphic-associations

ruby-on-rails - many - rails foreign key



Rails Polymorphic Association con mĂșltiples asociaciones en el mismo modelo (10)

Mi pregunta es esencialmente la misma que esta: Asociación polimórfica con múltiples asociaciones en el mismo modelo

Sin embargo, la solución propuesta / aceptada no funciona, como lo ilustra un comentarista posterior.

Tengo una clase de fotografía que se utiliza en toda mi aplicación. Una publicación puede tener una sola foto. Sin embargo, quiero reutilizar la relación polimórfica para agregar una foto secundaria.

Antes de:

class Photo belongs_to :attachable, :polymorphic => true end class Post has_one :photo, :as => :attachable, :dependent => :destroy end

Deseado:

class Photo belongs_to :attachable, :polymorphic => true end class Post has_one :photo, :as => :attachable, :dependent => :destroy has_one :secondary_photo, :as => :attachable, :dependent => :destroy end

Sin embargo, esto falla ya que no puede encontrar la clase "SecondaryPhoto". Basado en lo que podría decir de ese otro hilo, me gustaría hacer:

has_one :secondary_photo, :as => :attachable, :class_name => "Photo", :dependent => :destroy

Excepto que se llama a Post # secondary_photo, simplemente devuelve la misma foto que se adjunta a través de la asociación de fotos, por ejemplo, Post # photo === Post # secondary_photo. Mirando el SQL, lo hace DONDE type = "Photo" en lugar de, digamos, "SecondaryPhoto" como me gustaría ...

¿Pensamientos? ¡Gracias!


Para uso mongoide esta solución

Tuve tiempos difíciles después de descubrir este problema pero obtuve una solución genial que funciona

Agregar a su Gemfile

gem ''mongoid-multiple-polymorphic''

Y esto funciona como un encanto:

class Resource has_one :icon, as: :assetable, class_name: ''Asset'', dependent: :destroy, autosave: true has_one :preview, as: :assetable, class_name: ''Asset'', dependent: :destroy, autosave: true end


¿Puedes agregar un modelo de SecondaryPhoto como:

class SecondaryPhoto < Photo end

y luego omita: class_name de has_one: secondary_photo?


Algo parecido a lo siguiente funcionó para las consultas, pero la asignación desde el usuario a la dirección no funcionó

Clase de usuario

has_many :addresses, as: :address_holder has_many :delivery_addresses, -> { where :address_holder_type => "UserDelivery" }, class_name: "Address", foreign_key: "address_holder_id"

Clase de dirección

belongs_to :address_holder, polymorphic: true


Lo he hecho en mi proyecto.

El truco es que las fotos necesitan una columna que se utilizará en la condición de has_one para distinguir entre fotos primarias y secundarias. Preste atención a lo que sucede en :conditions aquí.

has_one :photo, :as => ''attachable'', :conditions => {:photo_type => ''primary_photo''}, :dependent => :destroy has_one :secondary_photo, :class_name => ''Photo'', :as => ''attachable'', :conditions => {:photo_type => ''secondary_photo''}, :dependent => :destroy

La belleza de este enfoque es que cuando creas fotos usando @post.build_photo , el tipo_de_la foto se rellenará automáticamente con el tipo correspondiente, como ''primary_photo''. ActiveRecord es lo suficientemente inteligente como para hacer eso.


Ninguna de estas soluciones parece funcionar en Rails 5. Por alguna razón, parece que el comportamiento en torno a las condiciones de asociación ha cambiado. Al asignar el objeto relacionado, las condiciones no parecen usarse en el inserto; solo cuando lees la asociación.

Mi solución fue anular el método setter para la asociación:

has_one :photo, -> { photo_type: ''primary_photo''}, as: ''attachable'', dependent: :destroy def photo=(photo) photo.photo_type = ''primary_photo'' super end


Ninguna de las respuestas anteriores me ayudó a resolver este problema, así que pondré esto aquí en caso de que alguien más se encuentre con esto. Usando Rails 4.2 +.

Cree la migración (suponiendo que ya tenga una tabla Addresses):

class AddPolymorphicColumnsToAddress < ActiveRecord::Migration def change add_column :addresses, :addressable_type, :string, index: true add_column :addresses, :addressable_id, :integer, index: true add_column :addresses, :addressable_scope, :string, index: true end end

Configura tu asociación polimórfica:

class Address < ActiveRecord::Base belongs_to :addressable, polymorphic: true end

Configure la clase desde donde se llamará a la asociación:

class Order < ActiveRecord::Base has_one :bill_address, -> { where(addressable_scope: :bill_address) }, as: :addressable, class_name: "Address", dependent: :destroy accepts_nested_attributes_for :bill_address, allow_destroy: true has_one :ship_address, -> { where(addressable_scope: :ship_address) }, as: :addressable, class_name: "Address", dependent: :destroy accepts_nested_attributes_for :ship_address, allow_destroy: true end

El truco es que debe llamar al método de compilación en la instancia de Order o la columna de scope no se completará.

Entonces esto NO funciona:

address = {attr1: "value"... etc...} order = Order.new(bill_address: address) order.save!

Sin embargo, esto SÍ FUNCIONA.

address = {attr1: "value"... etc...} order = Order.new order.build_bill_address(address) order.save!

Espero que ayude a alguien más.


No lo usé, pero busqué en Google y estudié las fuentes de Rails y creo que lo que estás buscando es :foreign_type . Pruébalo y di si funciona :)

has_one :secondary_photo, :as => :attachable, :class_name => "Photo", :dependent => :destroy, :foreign_type => ''SecondaryPost''

Creo que el tipo en su pregunta debería ser Post lugar de Photo y, respectivamente, sería mejor usar SecondaryPost como asignado al modelo de Post .

EDITAR:

La respuesta anterior es completamente incorrecta. :foreign_type está disponible en el modelo polimórfico en la asociación belongs_to para especificar el nombre de la columna que contiene el tipo de modelo asociado.

Cuando miro en las fuentes de Rails, esta línea establece este tipo para la asociación:

dependent_conditions << "#{reflection.options[:as]}_type = ''#{base_class.name}''" if reflection.options[:as]

Como puede ver, usa base_class.name para obtener el nombre de tipo. Por lo que sé, no puedes hacer nada con eso.

Entonces mi sugerencia es agregar una columna al modelo de Foto, por ejemplo: photo_type . Y ajústelo a 0 si es la primera foto, o ajústelo a 1 si es la segunda foto. En sus asociaciones agregue :conditions => {:photo_type => 0} y :conditions => {:photo_type => 1} , respectivamente. Sé que no es una solución que está buscando, pero no puedo encontrar nada mejor. Por cierto, ¿tal vez sería mejor usar simplemente has_many association?


Rails 4.2+

class Photo belongs_to :attachable, :polymorphic => true end class Post has_one :photo, :as => :attachable, :dependent => :destroy has_one :secondary_photo, -> { where attachable_type: "SecondaryPhoto"}, class_name: Photo, foreign_key: :attachable_id, foreign_type: :attachable_type, dependent: :destroy end

Debe proporcionar foreign_key de acuerdo con .... able''ness o Rails solicitará la columna post_id en la tabla de fotos. La columna Attachable_type se llenará con la magia de Rails como SecondaryPhoto


Vas a tener que parchear la noción de tipo_extranjero en una relación de has_one. Esto es lo que hice para has_many. En un nuevo archivo .rb en su carpeta de inicializadores, llamé a mine add_foreign_type_support.rb. Le permite especificar cuál será su attachable_type. Ejemplo: has_many photo,: class_name => "Picture",: as => attachable,: foreign_type => ''Pic''

module ActiveRecord module Associations class HasManyAssociation < AssociationCollection #:nodoc: protected def construct_sql case when @reflection.options[:finder_sql] @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) when @reflection.options[:as] resource_type = @reflection.options[:foreign_type].to_s.camelize || @owner.class.base_class.name.to_s @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " @finder_sql += "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(resource_type)}" else @finder_sql += ")" end @finder_sql << " AND (#{conditions})" if conditions else @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" @finder_sql << " AND (#{conditions})" if conditions end if @reflection.options[:counter_sql] @counter_sql = interpolate_sql(@reflection.options[:counter_sql]) elsif @reflection.options[:finder_sql] # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */ @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (///*.*?/*// )?(.*)/bFROM/b/im) { "SELECT #{$1}COUNT(*) FROM" } @counter_sql = interpolate_sql(@reflection.options[:counter_sql]) else @counter_sql = @finder_sql end end end end end # Add foreign_type to options list module ActiveRecord module Associations # :nodoc: module ClassMethods private mattr_accessor :valid_keys_for_has_many_association @@valid_keys_for_has_many_association = [ :class_name, :table_name, :foreign_key, :primary_key, :dependent, :select, :conditions, :include, :order, :group, :having, :limit, :offset, :as, :foreign_type, :through, :source, :source_type, :uniq, :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove, :extend, :readonly, :validate, :inverse_of ] end end


Referencia futura para las personas que revisan esta publicación

Esto se puede lograr usando el siguiente código ...

Rails 3:

has_one :banner_image, conditions: { attachable_type: ''ThemeBannerAttachment'' }, class_name: ''Attachment'', foreign_key: ''attachable_id'', dependent: :destroy

Rails 4:

has_one :banner_image, -> { where attachable_type: ''ThemeBannerAttachment''}, class_name: ''Attachment'', dependent: :destroy

No estoy seguro de por qué, pero en Rails 3, debe proporcionar un valor de foreign_key junto con las condiciones y class_name. No use ''as:: attachable'' ya que esto utilizará automáticamente el nombre de la clase llamante cuando configure el tipo polimórfico.

Lo anterior también se aplica a has_many.