Question

Je me demandais quel était le meilleur moyen de modéliser une relation dans laquelle un objet est associé à exactement n objets d'une autre classe. Je souhaite étendre la relation has_one à une valeur spécifique de n.

Par exemple, une TopFiveMoviesList appartiendrait à un utilisateur et aurait exactement cinq films. J'imagine que la table SQL sous-jacente aurait des champs tels que movie_id_1, movie_id_2, ... movie_id_5.

Je sais que je pourrais créer beaucoup de relations et limiter le nombre d'enfants au niveau du modèle, mais je préférerais ne pas avoir de table intermédiaire.

Était-ce utile?

La solution

Mon premier réflexe serait d'utiliser une table de jointure, mais si cela n'était pas souhaitable, les colonnes User.movie [1-5] _id conviendraient parfaitement. (Je pense que movie1_id correspond mieux à la convention Rails que movie_id_1 .)

Puisque vous avez balisé Rails et ActiveRecord, je vais ajouter du code de modèle totalement non testé et probablement quelque peu faux à ma réponse. :)

class User < ActiveRecord::Base
  TOP_N_MOVIES = 5
  (1..TOP_N_MOVIES).each { |n|  belongs_to "movie#{n}".to_sym, :class_name => Movie }
end

Vous pouvez envelopper la ligne dans une méthode de style macro, mais si ce n'est pas le cas, cela rendra probablement votre code plus difficile à lire avec peu d'avantages pour DRY.

Vous pouvez également ajouter des validations pour vous assurer qu'il n'y a pas de films en double dans la liste des utilisateurs.

Associer votre classe de film à vos utilisateurs est similaire.

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

(Bien sûr que users_list_as_top_anything serait probablement mieux écrit en SQL explicite. Je suis paresseux aujourd'hui.)

Autres conseils

Je pense que la meilleure solution est de mettre en œuvre ce modèle via un modèle de jointure. Il permet au modèle List de s’inquiéter de la logique de liste et le modèle Film de s’inquiéter de la logique Movie. Vous pouvez créer un modèle Nomination (le nom n'est pas le plus grand, mais vous voyez ce que je veux dire) pour gérer la relation entre les films et les listes. Lorsqu'il y a une limite de 5, vous pouvez simplement limiter la nombre de nominations que vous retirez.

Je pense que cette approche est meilleure pour plusieurs raisons.

Premièrement, en supposant que vous souhaitiez pouvoir traverser les relations dans les deux sens ( movie.lists et list.movies ), l'approche à 5 colonnes sera beaucoup plus désordonné.

Même s'il serait bien mieux pour ActiveRecord de prendre en charge a n relations, ce n'est pas le cas et vous allez donc vous battre contre le framework. De plus, la relation a n me semble un peu fragile dans cette situation. Je n'ai pas vu ce type d'implémentation aboutir dans ActiveRecord, même si cela m'intéresserait vraiment. :)

Je suppose que vous voulez dire "mettre en oeuvre". plutôt que "modèle"? La modélisation en UML est relativement facile, par exemple, lorsque vous avez une entité Personne composée de 5 entités Movie.

Mais la difficulté vient lorsque vous dites has_one, allez à has_5. S'il s'agit d'une simple valeur scalaire, has_one est peut-être une propriété de l'entité parent. Has_5 est probablement 2 entités liées l'une à l'autre par le biais d'un " est composé de " relation en UML.

La principale question à laquelle vous devez répondre est probablement "Pouvez-vous garantir qu'il sera toujours classé dans le" Top 5 "?" Si oui, modélisez-le avec des colonnes, comme vous l'avez mentionné. Si non, modélisez-le avec une autre entité.

Une autre question est peut-être: "Est-ce que ce sera facile de refactoriser?" Si c'est simple, commencez avec 5 colonnes et refactorisez pour séparer les entités si cela change.

Comme d'habitude, "mieux" dépend de l'environnement commercial et technique.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top