Рубин на рельсах:Вложенные атрибуты, отношение «принадлежит_to»
-
22-09-2019 - |
Вопрос
У меня есть сущность «Пользователь» с полем «Текущее местоположение» (город и страна).Чтобы хранить эту информацию, я создал объект под названием 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 должен находиться на одной или нескольких сторонах связи.
Вот некоторые возможные решения:
Переместите Accepted_nested_attributes_for в модель Location и создайте формы наоборот.
-form_for @location do |location_form| ... =location_form.fields_for @user do |user_form| ....
К сожалению, это не позволяет логически представить информацию.И затрудняет редактирование нужного пользователя.
Используйте модель соединения и создайте связь has one:through.
Честно говоря, я не уверен, насколько хорошо Accept_nested_attributes_for работает с отношением :through, но это определенно решит вашу проблему со связыванием записей.
Игнорируйте 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!