Associazione has_n di ActiveRecord
-
03-07-2019 - |
Domanda
Mi chiedevo quale sia il modo migliore per modellare una relazione in cui un oggetto è associato esattamente con n oggetti di un'altra classe. Voglio estendere la relazione has_one a un valore specifico di n.
Ad esempio, una TopFiveMoviesList appartiene all'utente e contiene esattamente cinque film. Immagino che la tabella sql sottostante avrebbe campi come movie_id_1, movie_id_2, ... movie_id_5.
So che potrei fare una relazione has_many e limitare il numero di figli a livello di modello, ma preferirei non avere una tabella intermedia.
Soluzione
Il mio primo istinto sarebbe quello di utilizzare una tabella di join, ma se ciò non fosse auspicabile le colonne User.movie [1-5] _id
si adatterebbero al conto. (Penso che movie1_id
si adatti meglio alla convenzione Rails rispetto a movie_id_1
.)
Dato che hai taggato questo Rails e ActiveRecord, aggiungerò un codice modello completamente non testato e probabilmente un po 'sbagliato alla mia risposta. :)
class User < ActiveRecord::Base
TOP_N_MOVIES = 5
(1..TOP_N_MOVIES).each { |n| belongs_to "movie#{n}".to_sym, :class_name => Movie }
end
Potresti avvolgere quella riga in un metodo in stile macro, ma a meno che non sia un modello comune per la tua applicazione, probabilmente questo renderà il tuo codice più difficile da leggere con un piccolo vantaggio DRY.
Potresti anche voler aggiungere convalide per assicurarti che non ci siano film duplicati nell'elenco di un utente.
Associare la tua classe di film ai tuoi utenti è simile.
class Movie < ActiveRecord::Base
(1..User::TOP_N_MOVIES).each do |n|
has_many "users_list_as_top_#{n}".to_sym, :class_name => User, :foreign_key => "movie#{n}_id"
end
def users_list_as_top_anything
ary = []
(1..User::TOP_N_MOVIES).each {|n| ary += self.send("users_list_as_top_#{n}") }
return ary
end
end
(Certo che users_list_as_top_anything
sarebbe probabilmente meglio scritto come SQL esplicito. Oggi sono pigro.)
Altri suggerimenti
Penso che implementare questo modello attraverso un modello di join sarà la soluzione migliore qui. Permette al modello List
di preoccuparsi della logica di List e al modello Movie
di preoccuparsi della logica di Movie. Puoi creare un modello Nomination
(il nome non è il massimo, ma sai cosa intendo) per gestire la relazione tra film ed elenchi e quando c'è un limite di 5, puoi semplicemente limitare numero di nomination che ritiri.
Ci sono alcuni motivi per cui penso che questo approccio sia migliore.
Innanzitutto, supponendo che tu voglia essere in grado di attraversare le relazioni in entrambi i modi ( movie.lists
e list.movies
), l'approccio a 5 colonne sarà molto Messier.
Anche se sarebbe molto meglio per ActiveRecord supportare ha n
relazioni, non è così, e quindi combatterai il framework su quello. Inoltre, la relazione ha n
mi sembra un po 'fragile in questa situazione. Non ho visto quel tipo di implementazione decollata in ActiveRecord, anche se sarei davvero interessato a vederlo accadere. :)
Suppongo che intendi " implementa " anziché "modello"? La modellazione è abbastanza semplice in UML, diciamo, dove hai un'entità Person che è composta da 5 entità Movie.
Ma la difficoltà arriva quando dici has_one, andando a has_5. Se è un semplice valore scalare, has_one è forse una proprietà sull'entità padre. Has_5 è probabilmente 2 entità correlate tra loro attraverso un "è costituito da" relazione in UML.
La domanda principale a cui rispondere è probabilmente, "Puoi garantire che sarà sempre la" Top 5 "? " Se sì, modellalo con le colonne, come hai detto. Se no, modellalo con un'altra entità.
Un'altra domanda è forse, "Quanto sarà facile riformattare?" Se è semplice, diamine, inizia con 5 colonne e refactoring per separare le entità se dovesse mai cambiare.
Come al solito, "migliore" dipende dall'ambiente aziendale e tecnico.