Рубин на рельсах:Вложенные атрибуты, отношение «принадлежит_to»

StackOverflow https://stackoverflow.com/questions/1593853

Вопрос

У меня есть сущность «Пользователь» с полем «Текущее местоположение» (город и страна).Чтобы хранить эту информацию, я создал объект под названием Location, у которого есть много пользователей.

Я не совсем уверен, следует ли мне указывать модель пользователя «has_one» или «belongs_to», но для того, что я читаю, если я хочу, чтобы у нее был внешний ключ местоположения, я должен указать «belongs_to».Я также хочу иметь возможность редактировать текущее местоположение пользователя при редактировании пользователя.поэтому я использую вложенные атрибуты.Но когда я редактирую пользователя, я каждый раз добавляю новое местоположение, даже не связывая его с редактируемым пользователем.Можете ли вы мне помочь?

Мой код следующий:

#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
Это было полезно?

Решение

Как отмечали другие, точная проблема, с которой вы столкнулись, заключается в том, что ваш контроллер не получает идентификатор местоположения должным образом.Мне кажется, идентификатор местоположения передается через неправильный параметр.К сожалению, идентификатор местоположения не существует в новой записи, поэтому в форме это невозможно.

Ваша проблема связана с использованием Accepts_nested_attributes_for в отношениях «принадлежит_to».Поведение четко не определено.Похоже, это задокументированная ошибка.Таким образом, Accepts_nested_attributes_for должен находиться на одной или нескольких сторонах связи.

Вот некоторые возможные решения:

  1. Переместите Accepted_nested_attributes_for в модель Location и создайте формы наоборот.

    -form_for @location do |location_form|
     ...
     =location_form.fields_for @user do |user_form|
       ....
    

    К сожалению, это не позволяет логически представить информацию.И затрудняет редактирование нужного пользователя.

  2. Используйте модель соединения и создайте связь has one:through.

    Честно говоря, я не уверен, насколько хорошо Accept_nested_attributes_for работает с отношением :through, но это определенно решит вашу проблему со связыванием записей.

  3. Игнорируйте Accepts_nested_attributes_for и обрабатывайте ассоциацию в вашем контроллере по старинке.

    На самом деле сохраните Accepts_nested_attributes_for.Он предоставляет несколько удобных методов, но не позволяйте ему добраться до оператора 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
    

Поля для автоматически заполняют поле идентификатора в хеше current_location_attributes, если не создается новое местоположение.Однако для работы find_or_create_by_id требуется запись :id в хэше.Он будет создан с правильно автоматически увеличенным идентификатором, если идентификатор отсутствует в базе данных.Если вы создаете новое местоположение, вам необходимо его добавить.Проще всего добавить его в форму с помощью =location_form.hidden_field :id, 0 unless current\_location.new\_record?.

Однако вы можете сократить создание дубликатов местоположений и изменить строку Location.find_or_create_by_id на Location.find_or_create_by_location.Это также сократит количество ошибок из-за неудачных проверок уникальности.

Другие советы

Вы не указываете идентификатор вложенного атрибута.Итак, Rails думает, что это новый.

- 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?

Не уверен, что предыдущий ответ действительно верен.Вам нужно указать идентификатор пользователя для местоположения, а не само местоположение.

- 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

По умолчанию belongs_to :current_location, :class_name => 'Location' будет ожидать Users на столе есть current_location_id поле.Получив это, вы сможете сделать что-то вроде:

@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!
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top