Ruby on Rails: Attributs Nested, belongs_to relation
-
22-09-2019 - |
Question
J'ai une entité utilisateur qui a un champ Emplacement actuel (ville et pays). Pour tenir cette information que j'ai créé une entité appelée Lieu qui has_many utilisateurs.
Je ne suis pas tout à fait sûr que je devrais mettre dans le modèle utilisateur « has_one » ou « belongs_to », mais pour ce que je lis si je voulais qu'il ait la clé étrangère de l'endroit que je devrais mettre « belongs_to ». Je veux aussi pouvoir modifier emplacement actuel de l'utilisateur lors de l'édition l'utilisateur. donc je suis en utilisant les attributs imbriqués. Mais quand je modifier l'utilisateur, je finis par l'ajout d'un nouvel emplacement à chaque fois sans jamais l'associer à l'utilisateur qui a été édité. Peux-tu m'aider?
Mon code est le suivant:
#User Model
class User < ActiveRecord::Base
## Relationships
belongs_to :current_location, :class_name => 'Location'
accepts_nested_attributes_for :current_location
end
#Location Model
class Location < ActiveRecord::Base
#Relationship
has_many :users
end
# part of the _form_edit.haml
- form_edit.fields_for :current_location do |location_form|
= location_form.label :location, "Current Location"
= location_form.text_field :location
#Application Helper
#nested attributes for user and location
def setup_user(user)
returning(user) do |u|
u.build_current_location if u.current_location.nil?
end
end
#in the user controller (added after edit)
def update
@user = @current_user
if @user.update_attributes(params[:user])
flash[:notice] = "Account updated!"
redirect_to account_url
else
render :action => :edit
end
end
La solution
Le problème exact que vous êtes confronté, comme d'autres l'ont souligné est que le contrôleur ne reçoit pas l'identifiant de l'emplacement comme il se doit. Ressemble me l'identifiant de l'emplacement est passé à travers le mauvais paramètre. Malheureusement, un identifiant de localisation n'existe pas sur un nouveau record, donc ce n'est pas possible sous la forme.
Votre problème vient du accepts_nested_attributes_for d'utilisation sur une relation de belongs_to. Le comportement est pas clairement défini. Cela semble être un bogue documenté. Ainsi, le accepts_nested_attributes_for devrait être sur a un ou a beaucoup d'autre d'une relation.
Voici quelques solutions possibles:
-
Déplacer le accepted_nested_attributes_for au modèle de localisation et de construire vos formulaires dans l'autre sens.
-form_for @location do |location_form| ... =location_form.fields_for @user do |user_form| ....
Malheureusement, cela ne permet pas une façon logique de présentation de l'information. Et rend l'édition de l'utilisateur droit difficile.
-
Utilisez une jointure modèle, et faire un a un. Par rapport
Je suis honnêtement ne sais pas comment fonctionne accept_nested_attributes_for avec: à travers une relation, mais cela certainement résoudre votre problème avec couplage des enregistrements
.
-
Ignorer accepts_nested_attributes_for et de gérer l'association dans votre contrôleur à l'ancienne.
En fait garder le accepts_nested_attributes_for. Il fournit des méthodes pratiques à portée de main, ne laissez pas arriver à les update_attributes / create.
def update @user = @current_user completed = false location_params = params[:user].delete(:current_location_attributes) User.transaction do @location = Location.find_or_create_by_id(location_params) @user.update_attributes(params[:user]) @user.current_location = @location @user.save! completed = true end if completed flash[:notice] = "Account updated!" redirect_to account_url else render :action => :edit end end
Les champs pour se remplir un champ id dans le hachage current_location_attributes automatiquement, si elle ne crée pas un nouvel emplacement. Cependant, find_or_create_by_id, nécessite une: entrée id dans le hachage pour que cela fonctionne. Il va créer un identifiant correctement incrémentée automatique si l'ID est pas dans la base de données. Si vous créez un nouvel emplacement, vous devez l'ajouter. Pour l'ajouter facile à la forme avec =location_form.hidden_field :id, 0 unless current\_location.new\_record?
.
Cependant, vous pouvez réduire la création de l'emplacement en double, et changer la ligne Location.find_or_create_by_id à Location.find_or_create_by_location. Cela permettra également de réduire les erreurs de l'unicité échec de validation.
Autres conseils
Vous ne fournissez pas l'identifiant de l'attribut imbriqué. Alors rails pense qu'il est un nouveau.
- form_edit.fields_for :current_location do |location_form|
= location_form.label :location, "Current Location"
= location_form.text_field :location
= location_form.hidden_field :id unless location_form.new_record?
Je ne sais pas si la réponse précédente est vraiment correcte. Ce que vous avez besoin est de spécifier l'ID de l'utilisateur pour l'emplacement, pas l'emplacement lui-même.
- form_edit.fields_for :current_location do |location_form|
= location_form.label :location, "Current Location"
= location_form.text_field :location
= location_form.hidden_field :user_id
Par belongs_to :current_location, :class_name => 'Location'
par défaut attendre la table Users
ont un champ de current_location_id
. Une fois que vous avez cela, vous devriez être en mesure de faire quelque chose comme:
@user = @current_user
@user.update_attributes(params[:user])
@location = @user.current_location or @user.build_current_location
@location.update_attributes(params[:location])
@user.current_location.save!
@user.save!