2010-02-04 3 views
2

J'ai un scénario dans lequel je souhaite enregistrer des données dans deux modèles d'un formulaire. Fondamentalement, j'ai un player qui appartient à beaucoup teams Donc, dans l'action new de players_controller je voudrais avoir une boîte de sélection multiple qui contient toutes les équipes. Ensuite, l'utilisateur peut sélectionner 2 ou 3 d'entre eux. Cliquez sur Enregistrer et ils seront sauvegardés.comment enregistrer des données dans deux modèles à partir d'un formulaire

player appartenant à plusieurs teams se fait par une table appelée playerizations contient une player_id et team_id colonnes

donc si je veux obtenir toutes les équipes un joueur appartient. Je viens de dire player.teams

toute cette relation fonctionne très bien. Je voudrais juste savoir comment économiser de playerizations table quand nouvelle player est créé

Ce que je (il est essentiellement modèle échafaudage):

def new 
    @player = Player.new 
@teams = Team.all 
    respond_to do |format| 
     format.html # new.html.erb 
     format.xml { render :xml => @player } 
    end 

Vue:

<% form_for(@player) do |f| %> 
    <%= f.error_messages %> 
... 
.... 
All Teams<br/> 
<%= select_tag 'selected_teams[]', options_for_select(@teams.map {|t| [t.team_name, t.id]}), :multiple => true%> 
     end 

Puis-je obtenir des indices s'il vous plaît? J'ai jeté un coup d'oeil à railscast à ce sujet mais pas beaucoup d'aide.

Répondre

2

Vous êtes en train de décrire une relation de type HABTM mais vous ne l'avez pas définie selon la convention de Rails. Rails ne sait donc pas comment mettre à jour vos modèles.

modèle du joueur doit dire:

has_and_belongs_to_many :teams 

modèle d'équipe devrait dire:

has_and_belongs_to_many :players 

Cela a pour effet secondaire heureux que non seulement « player.teams » donnent une liste des associés d'un joueur équipes, mais aussi "team.players" donne la liste des joueurs dans une équipe donnée.

Votre table de jointure doit être appelée "players_teams", car la convention Rails consiste à utiliser le nom des deux modèles au pluriel et à les assembler par ordre alphabétique croissant. Renommer votre table "playerizations" devrait être suffisant car il semble que les colonnes de la table sont correctes.

Votre code de menu de sélection est presque là; vous avez besoin quelque chose comme:

select_tag(
    :player_team_ids, 
    options_for_select(@teams.map { | t | [ t.team_name, t.id ] }), 
    { 
    :multiple => true, 
    :name  => 'player[team_ids][]' 
    } 
) 

Il est l'affectation « nom » qui contient la « magie » pour obtenir votre équipe tableau ID attribués. Le premier paramètre de "select_tag" est juste le nom du champ de formulaire "player [team_ids] []" avec les crochets transformés en underscore ou supprimés si à la fin de la chaîne, générant ainsi un ID reconnaissable et unique à utiliser dans la sortie HTML.

Vous pouvez ensuite enregistrer votre modèle de lecteur ou mettre à jour ses attributs avec des appels standards à save() ou update_attributes() - pas besoin de code supplémentaire en soi, mais Rails faiblit lors des validations. Si vous modifiez les détails d'un joueur existant, un appel à "update_attributes" entraînera la mise à jour de l'association des équipes en premier. Ensuite, le joueur est mis à jour. Si ses validations échouent, les changements d'équipes auront été sauvegardés de toute façon.C'est assez simple de patcher; Enveloppez votre appel à update_attributes() dans une transaction et annulez si update_attributes renvoie 'false' indiquant un échec.

success = Player.transaction do 
    player.update_attributes(params[ :player ])) or raise ActiveRecord::Rollback 
end 

La valeur de la « réussite » finira par être « vrai » pour le succès ou « nul » pour l'échec. Cela fonctionne car l'exception Rollback est interceptée par le bloc de transaction et ne se propage pas. Définir 'succès' au résultat évalué du bloc plutôt que d'essayer d'utiliser des variables locales signifie que le code est à la fois Ruby 1.8 et Ruby 1.9-friendly.

Ceci n'est nécessaire que lorsque met à jour les enregistrements existants avec des relations HABTM. Il n'est pas nécessaire lorsque créer de nouveaux enregistrements.

Tout le code ci-dessus n'a pas été testé et peut contenir des erreurs de frappe. Veuillez donc l'utiliser avec soin et attention.

Pour en savoir plus sur HABTM:

Pour en savoir plus sur les transactions:

+0

Il peut aussi le faire sans relation HABTM. Il peut utiliser 'has_many: through' et je pense qu'il l'a fait de cette façon, parce qu'il dit que ça fonctionne. Je pense que votre boîte de sélection fonctionnera toujours avec les relations spécifiées de cette façon. – klew

+0

HM: T devrait fonctionner et est favorisé dans Rails sur HABTM en ce moment - mais on ne sait pas si c'est une mode passagère (ils se produisent beaucoup dans Rails!). L'O.P. demande également "comment enregistrer dans la table des joueurs", ce qui implique que leurs relations associatives ne fonctionnent pas correctement. La documentation de l'API et les guides Rails indiquent que sémantiquement, HABTM doit être utilisé lorsque la table de jointure ne vous intéresse pas (comme cela semble être le cas avec OP) alors que HM: T est utilisé lorsque la table de jointure contient des informations supplémentaires. ne semble pas être le cas ici). Alors c'est peut-être plus une question de style ...! –

Questions connexes