2010-09-14 7 views
0

J'ai deux modèles ci-dessous. Ils peuvent être expliqués comme suit:Association ActiveRecord: créer une nouvelle association ou une référence existante si les attributs associés correspondent

Un rapport a un rapport_détail (qui détermine les mois de début/fin). De nombreux rapports peuvent avoir le même détail de rapport, mais aucun détail de rapport ne peut être identique.

class Report < ActiveRecord::Base 
    # attr: name :: String 
    # attr: report_detail_id :: Integer 
    belongs_to :report_detail 
    accepts_nested_attributes_for :report_detail 
end 

class ReportDetail < ActiveRecord::Base 
    # attr: duration :: Integer 
    # attr: starting_month :: Integer 
    # attr: offset :: Integer 
end 

J'ai une contrainte unique sur un index sur ReportDetail pour [: durée: starting_month,: offset]

Ce que je suis en train d'accomplir est la suivante: Si un nouveau rapport a ReportDetail qui a une combinaison unique attrs (: duration,: starting_month,: offset), crée le nouveau ReportDetail et sauvegarde comme d'habitude. Si un rapport possède un ReportDetail dans lequel un objet ReportDetail existant a les mêmes attributs, associez les détails du rapport à ce ReportDetail et enregistrez le rapport.

J'ai eu que cela fonctionne par aliasing le poseur sur report_detail= en utilisant un ReportDetail.find_or_create_by... mais il est laid (et il crée également des entrées de ReportDetail inutiles mais juste par instanciation de nouveaux rapports avec les attributs de détail, et pour une raison que je ne pouvais pas obtenir le sauver pour fonctionner correctement en utilisant .find_or_initialize_by...). J'ai également essayé un before_save sur le ReportDetail pour dire, si je fais correspondre quelque chose d'autre, mettez self à cet autre chose. Apparemment, vous ne pouvez pas vous définir comme ça.

Une idée de la meilleure façon de faire?

voir this gist pour mon setter actuel écrasera avec alias

+0

Pouvez-vous publier le code pour aliaser le setter? Peut-être en tant qu'amplification et lien ici. On dirait que c'était sur la bonne voie, mais il peut y avoir un bug ou deux. –

+0

ajouté ... il n'y a pas de bug dans ce code, tout fonctionne, mais je cherche une solution plus agréable. Mon plus gros boeuf est qu'il crée réellement un ReporDetail quand vous initialisez en utilisant l'essentiel ci-dessus. Je ne pouvais pas non plus le faire fonctionner correctement avec find_or_initialize_by car l'enregistrement sur l'association est censé avoir lieu avant cela, donc il ne finit pas de sauvegarder l'association dès le premier coup – brad

Répondre

1

juste couru aujourd'hui à ce problème, ma solution est basée à la méthode object_attributes=, qui est fourni par accepts_nested_attributes_for, je pense que cela crée moins de dégâts au lieu de passer outre la méthode de définition d'association standard. Plongé dans les rails de la source un peu pour trouver cette solution, voici le github link. Le code:

class Report < ActiveRecord::Base 
    # attr: name :: String 
    # attr: report_detail_id :: Integer 
    belongs_to :report_detail 
    accepts_nested_attributes_for :report_detail 

    def report_detail_attributes=(attributes) 
    report_detail = ReportDetail.find_or_create_by_duration_and_display_duration_and_starting_month_and_period_offset(attrs[:duration],attrs[:display_duration],attrs[:starting_month],attrs[:period_offset]) 
    attributes[:id]=report_detail.id 
    assign_nested_attributes_for_one_to_one_association(:report_detail, attributes) 
    end 
end 

Quelques explications, si vous fournissez un identifiant, sera considérée comme mise à jour, donc ne crée plus de nouveaux objets. Aussi, je sais que cette approche nécessite une requête en plus, mais je ne peux pas trouver une meilleure solution pour le moment.

En outre, il semble que vous avez association has_one entre le rapport et les détails du rapport, si tel est votre cas, vous pouvez essayer ceci:

class Report < ActiveRecord::Base 
     # attr: name :: String 
     # attr: report_detail_id :: Integer 
     has_one :report_detail 
     accepts_nested_attributes_for :report_detail, :update_only=>true 
    end 

accoring à documentation cela devrait fonctionner pour vous. De la documentation rails3:

: update_only

Permet de spécifier qu'un enregistrement existant ne peut être mis à jour. Un nouvel enregistrement ne peut être créé que si n'existe pas. Cette option ne fonctionne que pour les associationset est ignorée pour les associations de collection . Cette option est désactivée par défaut.

J'espère que cela aide, de toute façon si vous trouvez une meilleure solution s'il vous plaît faites le moi savoir.

+0

Désolé de ne pas avoir répondu/accepté, j'ai commuté à autre chose, n'ont pas eu l'occasion de tester, mettra à jour quand je fais, merci pour la réponse! – brad

Questions connexes