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
Foi útil?

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:

  1. 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.

  2. 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.

  3. 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!
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top