Ruby on Rails: nidificati Attributi, belongs_to relazione
-
22-09-2019 - |
Domanda
Ho un'entità utente che ha un campo della posizione corrente (città e paese). Per mantenere queste informazioni ho creato un'entità chiamata Location che has_many utenti.
Io non sono del tutto sicuro se devo mettere nel modello User "has_one" o "belongs_to", ma per quello che ho letto, se ho voluto avere la chiave esterna della posizione devo mettere "belongs_to". Voglio anche essere in grado di modificare l'utente posizione corrente quando si modifica l'utente. così io sto usando attributi nidificati. Ma quando ho modificare l'utente finisco per l'aggiunta di una nuova posizione ogni volta senza mai associare all'utente che è stato modificato. Puoi aiutarmi?
Il mio codice è il seguente:
#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
Soluzione
Il problema esatto che stai affrontando, come altri hanno fatto notare è che il controller non riceve la posizione id come dovrebbe. Sembra a me l'id posizione viene fatto passare attraverso il parametro errato. Purtroppo una posizione id non esiste un nuovo record, quindi questo non è possibile in forma.
Il problema nasce dal accepts_nested_attributes_for uso su un rapporto belongs_to. Il comportamento non è chiaramente definito. Questo sembra essere un problema documentato. Così l'accepts_nested_attributes_for dovrebbe essere su un ha una o ha molti lato di una relazione.
Ecco alcune soluzioni possibili:
-
Sposta l'accepted_nested_attributes_for al modello posizione e costruire i moduli viceversa.
-form_for @location do |location_form| ... =location_form.fields_for @user do |user_form| ....
Purtroppo questo non consente un modo logico di presentare le informazioni. E rende la modifica del diritto utente difficile.
-
Utilizzare un modello di unirsi, e fare una ha uno:. Attraverso la relazione
Io non sono onestamente sicuro come accept_nested_attributes_for funziona con: attraverso la relazione, ma sarà sicuramente risolvere il tuo problema con il collegamento record
.
-
Ignora accepts_nested_attributes_for e gestire l'associazione nel controller alla vecchia maniera.
In realtà il mantenere accepts_nested_attributes_for. Esso fornisce alcuni metodi di convenienza a portata di mano, basta non lasciarlo arrivare alle update_attributes / creare comunicato.
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
I campi per popoleranno un campo ID nel current_location_attributes hash automaticamente, se non è la creazione di una nuova posizione. Tuttavia, find_or_create_by_id, richiede un: ingresso id nella hash per farlo funzionare. Si creerà un modo corretto di auto incrementato id se l'ID non è presente nel database. Se si sta creando una nuova posizione è necessario aggiungerlo. Più semplice per aggiungere al modulo con =location_form.hidden_field :id, 0 unless current\_location.new\_record?
.
Tuttavia, si potrebbe desiderare di ridurre la creazione di posizione duplicato, e cambiare la linea Location.find_or_create_by_id a Location.find_or_create_by_location. Questo sarà anche ridurre eventuali errori di validazione unicità falliti.
Altri suggerimenti
Non fornire l'ID dell'attributo nidificato. Così rotaie pensa che sia una nuova.
- 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?
Non so se la risposta precedente è propriamente corretto. Quello che vi serve è quello di specificare l'ID dell'utente per la posizione, non la posizione in sé.
- 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
Per impostazione predefinita belongs_to :current_location, :class_name => 'Location'
si aspettano tavolo Users
hanno un campo current_location_id
. Una volta che hai questo si dovrebbe essere in grado di fare qualcosa di simile:
@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!