Rubi nos trilhos: atributos aninhados, pertence a relação
-
22-09-2019 - |
Pergunta
Eu tenho uma entidade de usuário que possui um campo de localização atual (cidade e país). Para manter essas informações, criei uma entidade chamada Location, que possui usuários.
Não tenho certeza se devo colocar no modelo de usuário "Has_one" ou "pertences_to", mas pelo que leio se quisesse ter a chave estrangeira do local que devo colocar "pertences_to". Também quero poder editar o local atual do usuário ao editar o usuário. Então, estou usando atributos aninhados. Mas quando edito o usuário, acabo adicionando um novo local a cada vez, sem nunca associá -lo ao usuário que foi editado. Você pode me ajudar?
Meu código é o seguinte:
#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
Solução
O problema exato que você está enfrentando, como outros apontaram é que seu controlador não está recebendo o ID do local como deveria. Parece -me que o ID do local está sendo passado pelo parâmetro errado. Infelizmente, um ID de localização não existe em um novo registro, portanto, isso não é possível no formulário.
Seu problema decorre do uso aceita_nested_attributes_for em um relacionamento pertencente. O comportamento não está claramente definido. Este parece ser um bug documentado. Portanto, o aceita_nested_attributes_for deve estar em um ou ter um lado de um relacionamento.
Aqui estão algumas soluções possíveis:
Mova o Accepted_Netest_Attributes_For para o modelo de localização e crie seus formulários para o contrário.
-form_for @location do |location_form| ... =location_form.fields_for @user do |user_form| ....
Infelizmente, isso não permite uma maneira lógica de apresentar informações. E dificulta a edição do usuário certo.
Use um modelo de junção e faça um tem um: através do relacionamento.
Sinceramente, não tenho certeza de quão bem aceita_nested_attributes_for funciona com um: através do relacionamento, mas definitivamente resolverá seu problema com a ligação de registros.
Ignore aceita_nested_attributes_for e lide com a associação em seu controlador da maneira antiga.
Na verdade, mantenha os aceitos_nested_attributes_for. Ele fornece alguns métodos de conveniência úteis, mas não deixe que ele chegue à instrução update_attributes/crie.
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
Os campos para preencherão um campo de identificação no hash curt_location_attributes automaticamente, se não estiver criando um novo local. No entanto, find_or_create_by_id, requer uma entrada: ID no hash para que funcione. Ele criará com um ID incrementado corretamente se o ID não estiver no banco de dados. Se você estiver criando um novo local, precisará adicioná -lo. Mais fácil de adicioná -lo à forma com =location_form.hidden_field :id, 0 unless current\_location.new\_record?
.
No entanto, convém reduzir a criação de localização duplicada e alterar o local.find_or_create_by_id linha para localização.find_or_create_by_location. Isso também reduzirá os erros de validações de singularidade com falha.
Outras dicas
Você não fornece o ID do atributo aninhado. Então Rails acha que é novo.
- 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?
Não tenho certeza se a resposta anterior está realmente correta. O que você precisa é especificar o ID do usuário para o local, não o próprio local.
- 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
Por padrão belongs_to :current_location, :class_name => 'Location'
vai esperar o Users
Tabela tem um current_location_id
campo. Depois de ter isso, você poderá fazer algo como:
@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!