我有一个具有当前位置字段(城市和国家/地区)的用户实体。为了保存这些信息,我创建了一个名为Location的实体has_many Users。
我不完全确定我是否应该输入用户模型“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
答案 0 :(得分:9)
正如其他人指出的那样,您遇到的确切问题是您的控制器没有收到应有的位置ID。在我看来,位置ID正在通过错误的参数传递。遗憾的是,新记录中不存在位置ID,因此表单中无法进行此操作。
您的问题源于在belongs_to关系上使用accepts_nested_attributes_for。行为没有明确定义。这似乎是一个记录在案的错误。所以accepts_nested_attributes_for应该是一个或者有很多关系。
以下是一些可能的解决方案:
将accepted_nested_attributes_for移动到Location模型并以相反的方式构建表单。
-form_for @location do |location_form|
...
=location_form.fields_for @user do |user_form|
....
不幸的是,这不允许提供信息的逻辑方式。并且使编辑成为合适的用户很困难。
使用联接模型,并建立一个:通过关系。
老实说,我不确定accept_nested_attributes_for对于:通过关系有多好,但它肯定会解决你的链接记录问题。
忽略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哈希中的id字段。但是,find_or_create_by_id在哈希中需要一个:id条目才能工作。如果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。这也将减少唯一性验证失败的任何错误。
答案 1 :(得分:0)
您不提供嵌套属性的ID。所以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?
答案 2 :(得分:0)
不确定之前的答案是否真的正确。您需要的是指定位置的用户ID,而不是位置本身。
- 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
答案 3 :(得分:0)
默认情况下,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!