2010-07-09 6 views
13

Utilizzo di Ruby on Rails, come è possibile ottenere una relazione polimorfa has_many in cui il proprietario è sempre noto ma gli elementi nell'associazione saranno di tipo polimorfico (ma omogeneo), specificato da una colonna nel proprietario? Ad esempio, supponiamo che la classe Producerhas_many prodotti, ma le istanze dei produttori potrebbero in realtà hanno molte biciclette, o ghiaccioli, o lacci delle scarpe. Posso facilmente avere ogni classe di prodotto (biciclette, Popsicle, etc.) hanno una relazione belongs_to ad un produttore, ma dato un esempio produttore come posso ottenere la collezione di prodotti, se sono di vario tipo (per esempio produttore)?Rails Polymorphic has_many

Rails associazioni polimorfiche consentono ai produttori di appartenere a molti prodotti, ma ho bisogno del rapporto di essere il contrario. Per esempio:

class Bicycle < ActiveRecord::Base 
    belongs_to :producer 
end 

class Popsicle < ActiveRecord::Base 
    belongs_to :producer 
end 

class Producer < ActiveRecord::Base 
    has_many :products, :polymorphic_column => :type # last part is made-up... 
end 

Quindi il mio tavolo produttore ha già una colonna "tipo" che corrisponde ad una certa classe di prodotto (ad esempio biciclette, Popsicle, ecc), ma come posso ottenere Rails di farmi fare qualcosa di simile:

>> bike_producer.products 
#=> [[email protected], [email protected], ...] 
>> popsicle_producer.products 
#=> [[email protected], [email protected], ...] 

Scusa se questo è ovvio o una ripetizione comune; Sto avendo difficoltà sorprendenti nel raggiungerlo facilmente.

+0

Proprio come una nota, vi consiglio vivamente l'uso di fabbrica come un nome del modello, come factory_girl è un'estensione molto comunemente usato utilizzato per la generazione di modelli, invece di infissi, e questo può essere molto confusa per le persone che leggono il vostro codice. –

+0

@jamie - grazie per il suggerimento, ho cambiato la terminologia in "Produttore", che si spera non venga confusa con nessuna libreria di concorrenza =) – maerics

+0

Nessun problema. Inoltre, non ho trovato una buona soluzione a questo. Per quanto ne so, la capacità di un oggetto di avere figli polimorfici non esiste ancora in Rails. Puoi comunque controllare http://blog.hasmanythrough.com/2006/4/3/polymorphic-through per riferimento. –

risposta

1

Ecco la soluzione Attualmente sto usando. Non fornisce uno dei metodi di convenienza (operazioni di raccolta) che si ottiene da veri ActiveRecord :: Associazioni, ma fornisce un modo per ottenere l'elenco dei prodotti per un determinato produttore:

class Bicycle < ActiveRecord::Base 
    belongs_to :producer 
end 

class Popsicle < ActiveRecord::Base 
    belongs_to :producer 
end 

class Producer < ActiveRecord::Base 
    PRODUCT_TYPE_MAPPING = { 
    'bicycle' => Bicycle, 
    'popsicle' => Popsicle 
    }.freeze 
    def products 
    klass = PRODUCT_TYPE_MAPPING[self.type] 
    klass ? klass.find_all_by_producer_id(self.id) : [] 
    end 
end 

Un altro aspetto negativo è che devo mantenere il mapping delle stringhe di tipo per digitare classi ma che potrebbe essere automatizzato. Tuttavia, questa soluzione sarà sufficiente per i miei scopi.

-1
class Note < ActiveRecord::Base 

belongs_to :note_obj, :polymorphic => true 
belongs_to :user 


end 


class Contact < ActiveRecord::Base 

belongs_to :contact_obj, :polymorphic => true 
belongs_to :phone_type 

end 



class CarrierHq < ActiveRecord::Base 


has_many :contacts, :as => :contact_obj 
has_many :notes, :as => :note_obj 


end 
+0

Puoi spiegare la tua risposta un po ', forse addirittura modificarla per usare la terminologia del produttore/prodotto dalla domanda? – maerics

+0

classe biciclette true fine classe Popsicle true fine classe Produttore : bicycle_obj has_many: Popsicle,: come =>: popsicle_obj fine utilizzare questo codice se avete qualche problema con l'utilizzo su un oggetto, quindi si prega di lasciare commenti su questo con il codice. – user386660

1

si prega di prendere sul formato

class Bicycle < ActiveRecord::Base 
    belongs_to :bicycle_obj,:polymorphic => true 
end 

class Popsicle < ActiveRecord::Base 
    belongs_to :popsicle_obj , :polymorphic => true 
end 

class Producer < ActiveRecord::Base 
    has_many :bicycles , :as=>:bicycle_obj 
    has_many :popsicle , :as=>:popsicle_obj 
end 

Usa questo codice. Se avete qualche problema con esso, si prega di lasciare un commento.

+0

Aspetta: perché hai postato due risposte? –

+0

la mia risposta commentata non era nel formato corretto, ecco perché do una risposta seprata. – user386660

+0

Grazie @Jamie Wong: per le revisioni in realtà sono il nuovo tipo di stackoverflow e non so sulla formattazione del testo – user386660

0

Trovo che le associazioni polimorfiche è sotto documentati in Rails. C'è un unico schema di tabella eredità, che è quello che ottiene la maggior parte della documentazione, ma se non si utilizza l'ereditarietà singola tabella, poi ci sono alcune informazioni mancanti.

L'associazione belongs_to possono essere attivate utilizzando il: polimorfico => true l'opzione. Tuttavia, a meno che non si sta utilizzando l'ereditarietà singola tabella, l'associazione has_many non funziona, perché avrebbe bisogno di conoscere la serie di tabelle che potrebbe avere una chiave esterna.

(Da quello che ho trovato), credo che la soluzione pulita è quella di avere un tavolo e modello per la classe base, e hanno la chiave esterna nella tabella di base.

create_table "products", :force => true do |table| 
    table.integer "derived_product_id" 
    table.string "derived_product_type" 
    table.integer "producer_id" 
    end 

    class Product < ActiveRecord::Base 
    belongs_to :producer 
    end 

    class Producer < ActiveRecord::Base 
    has_many :products 
    end 

Poi, per un oggetto di produzione, produttore, si dovrebbe ottenere i prodotti con producer.products.derived_products.

Non ho ancora giocato con has_many fino a condensare l'associazione in producer.derived_products, quindi non posso commentare di farlo funzionare.

6

Devi usare STI sui produttori, non sui prodotti. In questo modo si ha un comportamento diverso per ogni tipo di produttore, ma in una singola tabella producers.

(quasi) Nessun polimorfismo!

class Product < ActiveRecord::Base 
    # does not have a 'type' column, so there is no STI here, 
    # it is like an abstract superclass. 
    belongs_to :producer 
end 

class Bicycle < Product 
end 

class Popsicle < Product 
end 

class Producer < ActiveRecord::Base 
    # it has a 'type' column so we have STI here!! 
end 

class BicycleProducer < Producer 
    has_many :products, :class_name => "Bicycle", :inverse_of => :producer 
end 

class PopsicleProducer < Producer 
    has_many :products, :class_name => "Popsicle", :inverse_of => :producer 
end 
+0

Il fatto che i prodotti ereditino dalla stessa classe genitore influenzi la loro variabilità? Ad esempio, la bicicletta potrebbe avere un attributo "frame_material" e Popsicle ha un "sapore"? – maerics

+0

@maerics Non esiste STI, tutti gli attributi provengono da ciascuna delle tabelle specifiche per le sottoclassi. – rewritten